Grouping a Collection
Grouping means sorting elements into buckets. You choose a key for every element, and all elements with the same key land in the same bucket.
The key is computed by a small expression that takes one element and returns its key. For example, the first letter of a word.
Python has no built-in group_by method on lists. The natural way to group is a dictionary whose keys are the group keys and whose values are lists of elements. You fill it with a for loop and setdefault(), or you let collections.defaultdict(list) create the empty lists for you.
A dictionary keeps its entries in insertion order, so the groups appear in the order their first elements were seen. The printed output always looks the same.
Group By Key
The simplest grouping keeps the elements as they are and only decides which bucket each one goes to.
setdefault(key, []) returns the list stored under the key, and stores a new empty list first when the key is not there yet. So groups.setdefault(key, []).append(w) works even for the first element of a group. The elements inside a group keep the order they had in the input.
Sometimes you want to change the elements while grouping, for example store them in upper case. Compute the key, compute the new value, and append the new value to the group. With a defaultdict(list) a plain groups[key].append(value) is enough.
Group words by first letter
The key is the first letter of the word, w[0], made upper case with upper(). Words that start with the same letter end up in the same list.
Output: complete Python program
words = ["one", "two", "three", "four", "five"]
# setdefault gives the list for a key, storing a new empty one first if needed
by_first_letter = {}
for w in words:
key = w[0].upper()
by_first_letter.setdefault(key, []).append(w)
print(by_first_letter)
Group and change the values
The key is the lower-case first letter. The stored value is the whole word in upper case. A defaultdict(list) starts every new group as an empty list; dict() turns it back into a plain dictionary for a clean print.
Output: complete Python program
from collections import defaultdict
words = ["one", "two", "three", "four", "five"]
# a defaultdict makes an empty list for every new key by itself
groups = defaultdict(list)
for w in words:
key = w[0] # lower-case first letter
value = w.upper() # the value stored in the group
groups[key].append(value)
print(dict(groups))
Count Each Group
Often you do not need the elements of each group, only how many there are.
The standard library has a tool for exactly this: collections.Counter. Give it the keys and it returns a dictionary-like object from key to count. Reading a key that is not in a Counter gives 0.
When it is printed, a Counter shows the biggest counts first.
Count words per first letter
Every word is turned into its first letter, and Counter counts the letters. The letters t and f both appear twice, so they are printed before o.
Output: complete Python program
from collections import Counter
words = ["one", "two", "three", "four", "five"]
# Counter counts every key for you; a missing key counts as 0
counts = Counter(w[0] for w in words)
print(counts)
Fold and Reduce per Group
Folding a group means combining its elements into one value, step by step. A running total is the classic example: start at 0 and add each element.
Python has no fold() or aggregate() on groups. Use a dictionary that holds the running value of every group. For each element, compute its key and update the value stored under that key.
When the running value has a starting value, such as 0 for a sum, get(key, 0) gives you that start for a missing key. When the first element itself must start the value, check with the in operator whether the key exists yet; get(key), which gives None for a missing key, works too.
Sum each group
Each number is put in the group "even" or "odd" and added to that group's sum. sums.get(key, 0) starts a new key at 0, so no special case is needed.
Output: complete Python program
numbers = [3, 4, 5, 6, 7, 8]
# get(key, 0) gives 0 for a new group, so adding works right away
sums = {}
for n in numbers:
key = "even" if n % 2 == 0 else "odd"
sums[key] = sums.get(key, 0) + n
print(sums)
Build a string per group
The first number of a group starts the string. Every later number is joined with "+". The key not in chains check tells the two cases apart. str() turns a number into text.
Output: complete Python program
numbers = [3, 4, 5, 6, 7, 8]
chains = {}
for n in numbers:
key = "even" if n % 2 == 0 else "odd"
# the first number of a group starts the string; later ones are added with "+"
if key not in chains:
chains[key] = str(n)
else:
chains[key] = chains[key] + "+" + str(n)
print(chains)