Skip to main content

How to Count Occurrences of an Item in a List in Python

Counting how many times a particular item appears in a list is one of the most common operations in Python programming. Depending on your situation, you might need to count a single specific value, count items that match a custom condition, or build a complete frequency distribution of every element. Python offers efficient built-in tools for each of these scenarios.

In this guide, you will learn multiple approaches to counting list items, understand when to use each one, and avoid common performance pitfalls that can slow down your code significantly.

Counting a Specific Item with .count()

The built-in .count() method is the simplest and most direct way to count how many times a single value appears in a list:

data = ["apple", "banana", "apple", "cherry", "apple"]

count = data.count("apple")

print(count)

Output:

3

This method is implemented in C under the hood, making it fast for single lookups. It runs in O(n) time, scanning the entire list once.

Keep in mind that .count() uses equality comparison, so it is case-sensitive for strings:

data = ["Apple", "apple", "APPLE"]

print(data.count("apple"))

Output:

1

Only the exact match "apple" is counted. If you need case-insensitive counting, normalize the list first:

data = ["Apple", "apple", "APPLE"]

count = sum(1 for item in data if item.casefold() == "apple")

print(count)

Output:

3

Counting Items That Match a Condition

To count items based on a custom rule rather than an exact value, use a generator expression with sum():

numbers = [5, 12, 15, 3, 8, 20]

count_above_ten = sum(1 for x in numbers if x > 10)

print(count_above_ten)

Output:

3

This works because the generator yields 1 for every element that satisfies the condition, and sum() adds them up. An equivalent alternative takes advantage of the fact that Python treats True as 1 and False as 0:

numbers = [5, 12, 15, 3, 8, 20]

count_above_ten = sum(x > 10 for x in numbers)

print(count_above_ten)

Output:

3

Both forms produce the same result. The sum(1 for ...) version is slightly more explicit about intent, while the sum(bool_expr for ...) version is more concise.

Counting All Unique Items with Counter

When you need frequency counts for every distinct element in a list, the Counter class from the collections module is the right tool. It processes the entire list in a single O(n) pass and returns a dictionary-like object:

from collections import Counter

data = ["a", "b", "a", "c", "b", "a"]

counts = Counter(data)

print(counts)
print(counts["a"])
print(counts["z"])

Output:

Counter({'a': 3, 'b': 2, 'c': 1})
3
0
note

Unlike a regular dictionary, Counter returns 0 for missing keys instead of raising a KeyError, which makes it safe to query any value without checking for existence first.

You can also retrieve the most frequent elements using .most_common():

from collections import Counter

data = ["a", "b", "a", "c", "b", "a"]
counts = Counter(data)

print(counts.most_common(2))

Output:

[('a', 3), ('b', 2)]

Avoiding the O(n squared) Trap

A common performance mistake is calling .count() repeatedly in a loop for multiple items. Each call scans the entire list, so the total time complexity becomes O(n * k), where k is the number of unique items. When most items are unique, this approaches O(n squared).

Here is the inefficient pattern:

# Inefficient: O(n * k) complexity
data = ["a", "b", "a", "c", "b", "a"]
unique_items = set(data)

for item in unique_items:
print(f"{item}: {data.count(item)}")

Output:

a: 3
b: 2
c: 1

While the output is correct, the code scans the full list once for every unique item. The correct approach builds a Counter once and then reads from it:

# Efficient: O(n) total complexity
from collections import Counter

data = ["a", "b", "a", "c", "b", "a"]
counts = Counter(data)

for item, count in counts.items():
print(f"{item}: {count}")

Output:

a: 3
b: 2
c: 1
warning

Each call to .count() scans the entire list from start to finish. For a list with n elements and k unique values, calling .count() for each unique item results in O(n * k) total operations. Use Counter instead whenever you need counts for more than one or two items.

Memory-Efficient Counting with Generators

When working with large datasets and you only need the count (not the filtered items themselves), a generator expression avoids creating an intermediate list in memory:

data = range(1_000_000)

# Memory efficient: no intermediate list created
count = sum(1 for x in data if x % 2 == 0)

print(count)

Output:

500000

Compare this to the list comprehension approach, which builds the entire filtered list in memory before measuring its length:

# Less efficient: creates a list of 500,000 elements first
count = len([x for x in data if x % 2 == 0])
tip

Use sum(1 for ...) instead of len([x for ...]) when you only need the count. The generator version uses constant memory regardless of how many items match the condition.

Counting with Complex Conditions

For more elaborate filtering logic, define a separate function and pass it to the generator expression:

def is_valid_email(s):
return isinstance(s, str) and "@" in s and "." in s

emails = ["user@example.com", "invalid", "test@site.org", "no-at-sign"]

valid_count = sum(1 for email in emails if is_valid_email(email))

print(valid_count)

Output:

2

This pattern keeps your counting logic clean and readable, even when the condition itself is complex. You can also reuse the validation function elsewhere in your codebase.

Counting Occurrences in a List of Dictionaries

A practical real-world scenario involves counting how many dictionaries in a list match a particular field value:

from collections import Counter

users = [
{"name": "Alice", "role": "admin"},
{"name": "Bob", "role": "user"},
{"name": "Carol", "role": "user"},
{"name": "Dave", "role": "admin"},
{"name": "Eve", "role": "moderator"},
]

role_counts = Counter(user["role"] for user in users)

print(role_counts)

Output:

Counter({'admin': 2, 'user': 2, 'moderator': 1})

The generator expression extracts the "role" value from each dictionary, and Counter tallies them in a single pass.

Method Comparison

GoalMethodTime Complexity
Single specific itemlist.count(x)O(n) per call
Items matching a conditionsum(1 for x in list if cond)O(n)
All items at onceCounter(list)O(n) total
Multiple lookups after countingCounter(list)[x]O(n) once, O(1) per lookup

Conclusion

Choose .count() when you need to count a single, specific value. It is the simplest and most readable option for that particular task. Use a generator expression with sum() when your counting criteria involve a condition rather than an exact match. Switch to collections.Counter whenever you need frequencies for multiple items or the complete distribution, since it processes the entire list in one pass and provides O(1) lookups afterward. Above all, avoid calling .count() inside a loop for multiple items, as this is a common source of unnecessary performance degradation that Counter eliminates entirely.