Skip to main content

Transforming a Collection

A transformation takes a collection, such as a slice or a map, and builds a new collection from it. The original is not changed.

In many languages, lists and maps come with ready-made methods like map, zip, associate and flatten. Go does not have these methods built in. Instead you write a small for loop, or a small helper function that you can reuse. That is the Go way, and it is short once you know the pattern.

Go has generics since version 1.18. A generic function works with any element type. The lessons on this page use generics (Go 1.21 or newer) so one helper works for numbers, strings and structs alike. The standard packages slices, maps and strings also help with common jobs.

Generic Map helper syntax

This is the shape of a reusable Map helper. T is the type of the input elements and R is the type of the result elements. The function f is called once for every element, and each answer is added to the new slice.

main.go
func Map[T, R any](items []T, f func(T) R) []R {
  result := make([]R, 0, len(items))
  for _, v := range items {
    result = append(result, f(v))
  }
  return result
}

Map

The map operation applies the same function to every element and collects the answers in a new slice of the same length. For example, it can turn [1 2 3] into [3 6 9].

Go has no map method on slices. You write a loop that calls the function for each element and uses append to add the result to a new slice. If you do this often, put the loop in a generic helper function.

Do not confuse this with Go's map type, which stores key:value pairs. Here "map" means "apply a function to every element".

Map with a helper function

The Map helper takes a slice and a function. It calls the function on every element and returns a new slice. Here it multiplies each number by 3.

Prompt
Write a generic helper function Map that applies a function to every element of a slice and returns a new slice. Use it on the slice 1, 2, 3 to multiply each number by 3 and print the result.
Output: Go program and captured output
Map with a helper function

Map with the index

Sometimes the position of an element matters too. In Go, range gives you both the index and the value, so a plain loop is enough. Here each value is multiplied by its own index.

Prompt
Loop over the slice 1, 2, 3 with range, using both the index and the value. Build a new slice where each element is value * index, then print it.
Output: Go program and captured output
Map with the index

Skip some results

Sometimes some results should be dropped. Go has no null values for numbers, so the helper's function returns two things: the result and a bool that says whether to keep it. When the bool is false, the element is skipped.

Prompt
Write a generic helper MapNotNil that takes a function returning a value and a bool, and keeps only the results where the bool is true. Use it on the slice 1, 2, 3: skip the number 2 and multiply the others by 3. Print the result.
Output: Go program and captured output
Skip some results

Transform map keys

To change the keys of a map, make a new map and copy every entry with the new key. Here strings.ToUpper turns each key into uppercase. fmt.Println prints a map with its keys sorted, so the output is always in the same order.

Prompt
Start with a map from "key1" to 1 and "key2" to 2. Build a new map with the same values but every key changed to uppercase using strings.ToUpper. Print the new map.
Output: Go program and captured output
Transform map keys

Transform map values

To change the values, do the same but keep the key and compute a new value. Here each value grows by the length of its key: 1 + len("key1") is 5.

Prompt
Start with a map from "key1" to 1 and "key2" to 2. Build a new map with the same keys where each value is the old value plus the length of its key. Print the new map.
Output: Go program and captured output
Transform map values

Zip

Zipping takes two slices and pairs up their elements by position: the first with the first, the second with the second, and so on. The result has as many pairs as the shorter slice.

Go has no zip function. You define a small struct to hold one pair, find the shorter length with the built-in min, and loop with an index to build the pairs.

Zip two slices

The Pair struct holds one color and one animal. The loop runs to the shorter length and builds one Pair per index. Printing a slice of structs shows each pair in curly braces.

Prompt
Define a Pair struct with a Color and an Animal field. Combine the slices red, brown, grey and fox, bear, wolf element by element into a slice of pairs, stopping at the shorter slice. Print the slice of pairs.
Output: Go program and captured output
Zip two slices

Zip with a transformation

You do not always need the pairs themselves. Here the loop uses both elements straight away to build a sentence. animals[i][:1] is the first letter, made uppercase with strings.ToUpper, and animals[i][1:] is the rest of the word.

Prompt
Combine the slices red, brown, grey and fox, bear, wolf element by element. For each pair print a sentence like "The Fox is red", with the first letter of the animal in uppercase.
Output: Go program and captured output
Zip with a transformation

Unzip a slice of pairs

Unzipping is the reverse: one slice of pairs becomes two slices. The loop reads each pair and appends its parts to two separate slices.

Prompt
Start with a slice of pairs: one with 1, two with 2, three with 3. Split it into a slice of words and a slice of numbers and print each slice on its own line.
Output: Go program and captured output
Unzip a slice of pairs

Associate

Associating builds a map from a slice. Each element gives you one key and one value. A map cannot hold the same key twice, so if two elements produce the same key, the later one wins.

Go has no associate functions. Make an empty map with make, then loop over the slice and assign result[key] = value for each element.

Elements as keys

The element itself becomes the key, and a value is computed from it. Here the words are keys and their lengths are values.

Prompt
Build a map from the words one, two, three where each word is a key and its length is the value. Print the map.
Output: Go program and captured output
Elements as keys

Elements as values

The element becomes the value, and the key is computed from it. Here the key is the uppercase first letter. Both two and three start with T, so three replaces two and the map has only two entries.

Prompt
Build a map from the words one, two, three where the key is the uppercase first letter of the word and the value is the word itself. Print the map and notice that "three" replaces "two" under the key T.
Output: Go program and captured output
Elements as values

Compute both key and value

You can compute both the key and the value from the element. Here the key is the first letter and the value is the word length. Again the later T entry wins.

Prompt
Build a map from the words one, two, three where the key is the uppercase first letter and the value is the length of the word. Print the map.
Output: Go program and captured output
Compute both key and value

Key and value from one element

Sometimes one element carries both parts. strings.Fields splits a full name on spaces into a slice of words. The last name becomes the key and the first name becomes the value.

Prompt
Start with the full names "Alice Adams" and "Brian Brown". Split each name into words with strings.Fields and build a map from last name to first name. Print the map.
Output: Go program and captured output
Key and value from one element

Flatten

Flattening turns a nested collection, such as a slice of slices, into one plain slice with all the elements in order.

Go has no flatten method, but append can add a whole slice at once: append(result, inner...). The three dots spread all elements of inner into the call.

Flatten a slice of slices

The loop visits each inner slice and appends all of its elements to result. Two inner slices of three numbers become one slice of six.

Prompt
Start with a slice of slices: 1, 2, 3 and 4, 5, 6. Append all inner elements into one flat slice using append with the ... operator, then print it.
Output: Go program and captured output
Flatten a slice of slices

Flatten values from structs

A flat map first takes a slice out of every element, then joins all those slices together. In Go you read the field you want, c.Values, and append it with the three dots. All values from the three containers end up in one slice.

Prompt
Define a Container struct with a Values field that is a slice of strings. Make three containers holding one, two, three; four, five; and six, seven, eight. Collect all their values into one slice and print it.
Output: Go program and captured output
Flatten values from structs

String Representation

Often you want to show a collection as readable text, for example with commas between the elements.

In Go, strings.Join takes a slice of strings and a separator and returns one string. The extras — a prefix, a postfix, a limit, or a transformation — are done with plain string concatenation or a short loop before joining. Numbers must be turned into strings first, for example with strconv.Itoa.

Join with a separator

strings.Join puts ", " between the words and returns one string.

Prompt
Join the words one, two, three into one string with ", " between them using strings.Join. Print the string.
Output: Go program and captured output
Join with a separator

Add a prefix and a postfix

A prefix and a postfix are just text around the joined result. In Go you add them with +.

Prompt
Join the words one, two, three with " | " between them, add the prefix "start: " and the postfix ": end", and print the result.
Output: Go program and captured output
Add a prefix and a postfix

Limit the number of elements

For a long slice you may want to show only the first few elements. Take a slice of the first 10 with numbers[:10], turn each into a string with strconv.Itoa, then add "...>" at the end to show that more elements were cut off.

Prompt
Make a slice with the numbers from 1 to 100. Join only the first 10 of them with ", " and add "<...>" at the end to show the list was cut. Print the result.
Output: Go program and captured output
Limit the number of elements

Transform each element before joining

To change every element before joining, build a new slice of strings in a loop, then call strings.Join on it. Here each word becomes "Element: " plus the word in uppercase.

Prompt
Turn each of the words one, two, three into "Element: " followed by the word in uppercase. Join the results with ", " and print the string.
Output: Go program and captured output
Transform each element before joining