Python · Syntax · Intermediate
collections: Counter, defaultdict, namedtuple
Specialized container types: Counter for counting, defaultdict for grouped data, namedtuple for structured records.
Quick topic start and explanations before exercises (exercises below):
deque (with maxlen) and namedtuple
#OrderedDict, ChainMap, and choosing the right collection
#Exercises:
Use `Counter` to count how many times each word appears in a list. Return the Counter object.
from collections import Counter
def word_count(words):
# your code here
pass
words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
c = word_count(words)
print(c['apple']) # 3
print(c['banana']) # 2
print(c['grape']) # 0 (Counter returns 0 for missing keys)
Solution
from collections import Counter
def word_count(words):
return Counter(words)
Return the 3 most common words in a list of words.
from collections import Counter
def top_three(words):
# your code here
pass
words = ['the', 'cat', 'sat', 'on', 'the', 'mat', 'the', 'cat', 'is', 'fat']
print(top_three(words)) # [('the', 3), ('cat', 2), ('sat', 1)] or similar
Solution
from collections import Counter
def top_three(words):
return Counter(words).most_common(3)
Group a list of words by their first letter using `defaultdict`. Return a dict where each key is a letter and the value is a list of words.
from collections import defaultdict
def group_by_letter(words):
# your code here
pass
words = ['apple', 'banana', 'avocado', 'blueberry', 'cherry']
result = group_by_letter(words)
print(result['a']) # ['apple', 'avocado']
print(result['b']) # ['banana', 'blueberry']
Solution
from collections import defaultdict
def group_by_letter(words):
groups = defaultdict(list)
for word in words:
groups[word[0]].append(word)
return dict(groups)
Use `defaultdict(int)` to count letter frequencies in a string. Return the defaultdict.
from collections import defaultdict
def letter_freq(s):
# your code here
pass
freq = letter_freq('hello')
print(freq['l']) # 2
print(freq['h']) # 1
print(freq['z']) # 0
Solution
from collections import defaultdict
def letter_freq(s):
freq = defaultdict(int)
for c in s:
freq[c] += 1
return freq
Use `OrderedDict` to create a cache that keeps the last 3 unique items added (LRU-like). Implement `add(key, value)` and `get_all()` methods.
from collections import OrderedDict
class SmallCache:
def __init__(self):
self.cache = OrderedDict()
self.max_size = 3
def add(self, key, value):
# your code here
pass
def get_all(self):
return list(self.cache.items())
c = SmallCache()
c.add('a', 1)
c.add('b', 2)
c.add('c', 3)
c.add('d', 4) # 'a' should be evicted
print(c.get_all()) # [('b', 2), ('c', 3), ('d', 4)]
Solution
from collections import OrderedDict
class SmallCache:
def __init__(self):
self.cache = OrderedDict()
self.max_size = 3
def add(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.max_size:
self.cache.popitem(last=False) # remove oldest
def get_all(self):
return list(self.cache.items())
Use a `deque` to implement a sliding window maximum: given a list of numbers and window size k, return a list of the maximum value in each window.
from collections import deque
def sliding_max(nums, k):
# your code here
pass
print(sliding_max([1, 3, -1, -3, 5, 3, 6, 7], 3))
# [3, 3, 5, 5, 6, 7]
Solution
from collections import deque
def sliding_max(nums, k):
result = []
window = deque() # stores indices
for i, n in enumerate(nums):
while window and nums[window[-1]] <= n:
window.pop()
window.append(i)
if window[0] <= i - k:
window.popleft()
if i >= k - 1:
result.append(nums[window[0]])
return result
Use `namedtuple` to create a `Point` type with `x` and `y` fields. Return a function that computes the distance between two Points.
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
def distance(p1, p2):
# your code here
pass
a = Point(0, 0)
b = Point(3, 4)
print(distance(a, b)) # 5.0
Solution
from collections import namedtuple
import math
Point = namedtuple('Point', ['x', 'y'])
def distance(p1, p2):
return math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2)
Given two Counters (word frequencies from two texts), return a new Counter with the combined word frequencies.
from collections import Counter
def combine_frequencies(c1, c2):
# your code here
pass
c1 = Counter({'apple': 3, 'banana': 1})
c2 = Counter({'apple': 2, 'cherry': 4})
result = combine_frequencies(c1, c2)
print(result['apple']) # 5
print(result['banana']) # 1
print(result['cherry']) # 4
Solution
from collections import Counter
def combine_frequencies(c1, c2):
return c1 + c2
Use `deque` with `maxlen` to keep only the last N items added to a stream. Implement a `StreamBuffer` class with `push(item)` and `get_recent()` methods.
from collections import deque
class StreamBuffer:
def __init__(self, maxlen):
# your code here
pass
def push(self, item):
# your code here
pass
def get_recent(self):
return list(self.buffer)
buf = StreamBuffer(3)
for x in [1, 2, 3, 4, 5]:
buf.push(x)
print(buf.get_recent()) # [3, 4, 5]
Solution
from collections import deque
class StreamBuffer:
def __init__(self, maxlen):
self.buffer = deque(maxlen=maxlen)
def push(self, item):
self.buffer.append(item)
def get_recent(self):
return list(self.buffer)
Given a list of transactions (each a dict with 'category' and 'amount'), use `defaultdict` to compute the total amount per category.
from collections import defaultdict
def totals_by_category(transactions):
# your code here
pass
txns = [
{'category': 'food', 'amount': 12.5},
{'category': 'travel', 'amount': 200.0},
{'category': 'food', 'amount': 8.0},
{'category': 'travel', 'amount': 50.0},
{'category': 'books', 'amount': 25.0},
]
result = totals_by_category(txns)
print(result['food']) # 20.5
print(result['travel']) # 250.0
Solution
from collections import defaultdict
def totals_by_category(transactions):
totals = defaultdict(float)
for t in transactions:
totals[t['category']] += t['amount']
return dict(totals)