Skip to main content

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 function that takes one element and returns its key. For example, the first letter of a word.

Go has no built-in groupBy method on slices. The natural way to group in Go is a map whose keys are the group keys and whose values are slices of elements: map[K][]T. You fill it with a for loop and append(), or you write the small generic helper below once and reuse it.

A map has no fixed order, but fmt.Println() prints a map sorted by key. So the printed output always looks the same.

Generic GroupBy helper syntax

T is the element type and K is the key type. K must be comparable because map keys must support ==. For each element the helper calls key() and appends the element to the slice stored under that key. Appending to a key that does not exist yet just starts a new slice.

main.go
func GroupBy[T any, K comparable](items []T, key func(T) K) map[K][]T {
  groups := make(map[K][]T)
  for _, v := range items {
    k := key(v)
    groups[k] = append(groups[k], v)
  }
  return groups
}

Group By Key

The simplest grouping keeps the elements as they are and only decides which bucket each one goes to.

Pass the slice and a key function to the GroupBy helper. The result is a map from key to the slice of elements with that key. 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. Go has no valueTransform parameter for this. Write the loop yourself: compute the key, compute the new value, and append the new value to the group.

Group words by first letter

The key function takes the first letter of the word with w[:1] and makes it upper case with strings.ToUpper(). Words that start with the same letter end up in the same slice.

Prompt
Write a generic GroupBy helper that takes a slice and a key function and returns a map from key to a slice of the elements with that key. Use it on the words one, two, three, four, five with the upper-case first letter as the key. Print the map.
Output: Go program and captured output
Group words 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 plain for loop does both steps and appends to groups[key].

Prompt
Group the words one, two, three, four, five by their lower-case first letter. Store each word in its group in upper case, not as it was. Use a loop and a map from string to a slice of strings, then print the map.
Output: Go program and captured output
Group and change the values

Count Each Group

Often you do not need the elements of each group, only how many there are.

Go has no groupingBy() or eachCount(). Use a map[string]int instead. Reading a key that is not in the map gives 0, so counts[key]++ works even for the first element of a group.

Count words per first letter

Every word adds 1 to the count of its first letter. The map ends up with one number per letter.

Prompt
Count how many of the words one, two, three, four, five start with each letter. Use a map from the first letter to an int and increase the count for every word. Print the map.
Output: Go program and captured output
Count words per first letter

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.

Go has no fold() or aggregate() on groups. The Go way is a map 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, a missing key already gives you that start. When the first element itself must start the value, check with the value, ok := m[key] pattern whether the key exists yet.

Sum each group

Each number is put in the group "even" or "odd" and added to that group's sum with sums[key] += n. A new key starts at 0, so no special case is needed.

Prompt
Take the numbers 3, 4, 5, 6, 7, 8 and put each one in the group "even" or "odd". Instead of keeping the numbers, add each number to the running sum of its group in a map from string to int. Print the map.
Output: Go program and captured output
Sum each group

Build a string per group

The first number of a group starts the string. Every later number is joined with "+". The exists flag from chains[key] tells the two cases apart. strconv.Itoa() turns a number into text.

Prompt
Take the numbers 3, 4, 5, 6, 7, 8 and put each one in the group "even" or "odd". For each group build a string step by step: the first number starts the string, and every later number is added with a "+" in front, giving text like 3+5+7. Print the map from group name to string.
Output: Go program and captured output
Build a string per group