Skip to main content

Filtering a Collection

Filtering means picking out the elements of a collection that match a condition.

The condition is written as a function that takes one element and returns true (keep it) or false (skip it). Such a function is called a predicate.

Filtering builds a new slice. The original slice is not changed.

Go has no built-in filter method on slices. You filter with a for loop and an if, or you write a small generic helper once and reuse it. The helper below works for a slice of any type.

The standard library has slices.DeleteFunc(), which removes elements that match a condition. Be careful: it changes the original slice instead of making a new one.

Generic Filter helper syntax

T stands for the element type. keep is the predicate: elements for which it returns true are copied into result.

main.go
func Filter[T any](items []T, keep func(T) bool) []T {
  var result []T
  for _, v := range items {
    if keep(v) {
      result = append(result, v)
    }
  }
  return result
}

Filter by Predicate

The simplest way to filter is to pass a predicate to the Filter helper. The helper walks through the slice and keeps every element that passes the test.

Maps have no helper of their own. To filter a map, loop over it with range and copy the matching key:value pairs into a new map.

If the condition also needs the position of the element, use the index that range gives you together with the value.

To keep the elements that do not match, put ! in front of the condition. Go has no separate filterNot.

A slice of type []any can hold values of different types. A type assertion such as v.(string) checks whether a value is a string and gives it back with that type. A pointer can be nil, which means "no value"; compare with nil to skip such entries.

Filter a slice

Keep only the words that are longer than 3 letters. The condition len(s) > 3 is passed to Filter as a function.

Prompt
Write a generic Filter helper that takes a slice and a condition function and returns a new slice with only the elements that pass. Use it on the words one, two, three, four to keep the words longer than 3 letters. Print the result.
Output: Go program and captured output
Filter a slice

Filter a map

Loop over the map and keep the pairs whose key ends with "1" and whose value is greater than 10. strings.HasSuffix() checks the end of a string.

Prompt
Create a map from the keys key1, key2, key3, key11 to the values 1, 2, 3, 11. Loop over it and copy into a new map only the pairs whose key ends with "1" and whose value is greater than 10. Print the new map.
Output: Go program and captured output
Filter a map

Filter using the index

range gives the index and the value. Keep a word only when its index is not 0 and it has fewer than 5 letters.

Prompt
Take the words one, two, three, four. Loop with range so you get both the index and the word. Keep a word only if its index is not 0 and it is shorter than 5 letters. Print the kept words.
Output: Go program and captured output
Filter using the index

Filter with a negated condition

Drop the words with 3 letters or fewer. The condition len(s) = 3 is negated with !, so only the longer words stay.

Prompt
Take the words one, two, three, four. Use a generic Filter helper, but negate the condition with ! so that words with 3 letters or fewer are dropped. Print the result.
Output: Go program and captured output
Filter with a negated condition

Filter by type

The slice holds nil, a number, two strings and a float. The type assertion v.(string) with the ok value finds the strings. Each one is printed in upper case.

Prompt
Make a slice of type []any holding nil, 1, "two", 3.0 and "four". Loop over it and use a type assertion to find the values that are strings. Print each string in upper case, one per line.
Output: Go program and captured output
Filter by type

Filter out nil values

The slice holds string pointers, and some of them are nil. Keep the pointers that are not nil and collect the values they point to with *p.

Prompt
Make a slice of string pointers that holds nil, a pointer to "one", nil, and a pointer to "two". Keep only the non-nil pointers and collect the strings they point to. Print the collected slice.
Output: Go program and captured output
Filter out nil values

Partition

Sometimes you want both groups: the elements that match and the ones that do not.

Go has no built-in partition. A small generic helper does it: it loops once and appends each element to either the match slice or the rest slice. The helper returns both slices, because a Go function can return more than one value.

Split a slice in two

Words longer than 3 letters go into match; all the others go into rest. Both slices are printed.

Prompt
Write a generic Partition helper that splits a slice into two: the elements that pass a condition and the rest. Use it on the words one, two, three, four with the condition "longer than 3 letters". Print the matching slice, then the rest.
Output: Go program and captured output
Split a slice in two

Test Predicates

Sometimes you do not need the matching elements, only a yes or no answer.

  • any: does at least one element pass the test? Go's slices.ContainsFunc() answers this.
  • none: does no element pass the test? Put ! in front of slices.ContainsFunc().
  • all: does every element pass the test? Go has no function for this, so use a small generic All helper.

Without a condition, "any element at all?" is len(s) > 0 and "no element at all?" is len(s) == 0.

On an empty slice, All returns true. There is no element that fails the test, so the test is never broken. This is called vacuous truth.

any

Check whether at least one word ends with "e", then check whether the slice has any element at all.

Prompt
Take the words one, two, three, four. Use slices.ContainsFunc to check whether at least one word ends with "e" and print the answer. Then print whether the slice has any element at all by checking its length is greater than 0.
Output: Go program and captured output
any

none

Check that no word ends with "a", then check whether the slice is empty.

Prompt
Take the words one, two, three, four. Use ! with slices.ContainsFunc to check that no word ends with "a" and print the answer. Then print whether the slice is empty by checking its length equals 0.
Output: Go program and captured output
none

all

Check whether every word ends with "e". Then call All on an empty slice, which gives true.

Prompt
Write a generic All helper that returns true only when every element of a slice passes a condition. Check whether all of the words one, two, three, four end with "e" and print the answer. Then call All on an empty slice with the same condition and print the answer.
Output: Go program and captured output
all