Kyle Banks

Creating Unique Slices in Go

Written by @kylewbanks on Jul 4, 2016.

Slices in Go are quite nice to work worth, but the standard library doesn't provide any way of creating a unique slice.

Since this is such a common requirement, the community has come up with a few tricks to accomplish this, and I figured I'd share the one that is the most common, and perhaps simplest to write.

Given a sample slice of integers like the following, how can we create a subset of the slice that contains only unique entries:

integers := []int{1, 2, 3, 3, 4, 5} // We want: []int{1, 2, 3, 4, 5}

The answer is actually quite simple, but requires the use of a map, which may not seem intuitive at first. Since maps, by definition, can only have unique keys, we can leverage this fact to create our unique slice:

package unique

// Ints returns a unique subset of the int slice provided.
func Ints(input []int) []int {
	u := make([]int, 0, len(input))
	m := make(map[int]bool)

	for _, val := range input {
		if _, ok := m[val]; !ok {
			m[val] = true
			u = append(u, val)
		}
	}

	return u
}

First up, we create a new slice u that will be our unique slice. We initialize it using the make function, with a length of zero and a capacity equal to the length of the input slice. Next we create a new map, again using the make function, where the key will be an integer (the type of slice we're dealing with) and the value will be a boolean. The value here doesn't actually matter, since we don't actually care about any values we're going to store in the map, we're only concerned with the keys.

Next we iterate over the input slice, using range. For each item in the slice, we use the two-value assignment of the map to check if the slice item exists as a key. Using the two-value assignment is important, because it allows us to check if the key exists. If it doesn't, we insert the item as a key in the map with a value of true. Again, the value is irrelevant, you could use a string/struct/integer/anything here, we just want a small datatype because a map has to have a value. After inserting the item into the map, we also append it to our unique slice. Finally, once the for-loop finishes, we return the unique slice.

Using the function is simple enough. Going back to our sample slice above:

integers := []int{1, 2, 3, 3, 4, 5}
unique := unique.Ints(integers)

fmt.Printf("Unique: %v", unique) // Unique: [1 2 3 4 5]

And that's it! Unfortunately the only drawback to this method is you will need a separate function for each slice type that you need unique functionality for. I've added integers to my go-kit on GitHub in the "unique" package, and I'm sure I'll be adding support for more datatypes as I need them.

Let me know if this post was helpful on Twitter @kylewbanks or down below, and follow me to keep up with future posts!