How to Find the Difference Between Two Slices in Go

Go's standard library has no set type, so "difference between two slices" always means building the hash-lookup yourself - usually with a map. Here are the three approaches worth knowing, from the everyday idiom to the sorted two-pointer version for when memory matters more than a few extra lines.

Quick answer

Build a map[string]struct{} from slice b, then loop over slice a and keep any element not present in the map - this is the standard Go idiom and runs in roughly O(n + m). With Go 1.18+, wrap it in a generic function constrained to comparable so it works for any comparable type, not just strings. If both slices are already sorted, a two-pointer scan gets the same O(n + m) result without allocating a map.

Need the same thing in another language?

See the full reference for Python, JavaScript, SQL, Java, and more.

See every language →

The map[string]struct{} idiom

a := []string{"apple", "banana", "cherry", "banana"}
b := []string{"banana", "date"}

setB := make(map[string]struct{}, len(b))
for _, v := range b {
    setB[v] = struct{}{}
}

var aOnly []string
for _, v := range a {
    if _, found := setB[v]; !found {
        aOnly = append(aOnly, v)
    }
}
fmt.Println(aOnly) // [apple cherry banana] - order preserved, duplicates preserved

struct{}{} is the idiomatic value type for a "set" in Go - it takes zero bytes of storage, since the map is only being used for its key lookup, not to store any associated value. This approach preserves both the order of a and any duplicate elements it contains, unlike a true set-based difference.

A generic Difference function (Go 1.18+)

func Difference[T comparable](a, b []T) []T {
    setB := make(map[T]struct{}, len(b))
    for _, v := range b {
        setB[v] = struct{}{}
    }

    var result []T
    for _, v := range a {
        if _, found := setB[v]; !found {
            result = append(result, v)
        }
    }
    return result
}

// Usage:
aOnly := Difference([]int{1, 2, 3}, []int{2, 3, 4}) // [1]

The comparable constraint (built into Go, no import needed) allows this function to work with any type that supports == - strings, numbers, and structs made entirely of comparable fields all qualify, without writing a separate function per type.

Why slices.Contains alone isn't enough

import "slices"

var aOnly []string
for _, v := range a {
    if !slices.Contains(b, v) {
        aOnly = append(aOnly, v)
    }
}
// Works, but slices.Contains is O(n) per call - the loop is O(n * m) overall

The standard library slices package (Go 1.21+) has no set-difference helper. slices.Contains does a linear scan each time it's called, so using it inside a loop over the other slice reintroduces the O(n * m) cost the map idiom exists to avoid - fine for small slices, worth replacing with a map for anything sizeable.

Sorted two-pointer scan: no map allocation

sort.Strings(a)
sort.Strings(b)

var aOnly []string
i, j := 0, 0
for i < len(a) {
    if j >= len(b) || a[i] < b[j] {
        aOnly = append(aOnly, a[i])
        i++
    } else if a[i] > b[j] {
        j++
    } else {
        i++
        j++
    }
}

Walking both sorted slices with two pointers finds the difference in a single O(n + m) pass with no map allocation at all - useful when memory is tighter than CPU time, or the slices are already sorted for another reason. If they aren't already sorted, sorting first costs O(n log n), at which point the map idiom is usually simpler to write for a one-off comparison.

Just need the answer, not the code? Paste both lists.

Open Compare 2 Lists →

Frequently Asked Questions

Does Go have a built-in set type for comparing slices?
No. The Go standard library has no set type. The idiomatic substitute is a map[T]struct{}, using an empty struct as the value since it takes zero bytes of storage - it exists purely to give you O(1) key lookups via the map.
Why use map[string]struct{} instead of map[string]bool?
struct{}{} occupies zero bytes, while bool occupies one byte per entry. For large slices this adds up in memory, and semantically struct{}{} communicates "this is a set, the value carries no information" more clearly than a bool that's always true.
How do I write a generic difference function in Go?
With Go 1.18+ generics: func Difference[T comparable](a, b []T) []T, constrained to the built-in comparable interface so it works with any type that supports == - strings, ints, and structs of comparable fields all qualify.
Does the slices package have a difference function?
No - the standard library slices package (Go 1.21+) has slices.Contains, slices.Index, and similar helpers, but no set-difference function. slices.Contains is a linear O(n) scan, so calling it inside a loop over another slice reintroduces the O(n * m) problem the map idiom avoids.
When is a sorted two-pointer approach better than a map?
When both slices are already sorted (or memory is tighter than CPU time), a two-pointer merge-style scan finds the difference in O(n + m) time without allocating a map at all. If the slices aren't already sorted, sorting them first costs O(n log n), which is usually still fine but makes the map approach simpler for a one-off comparison.
How do I get the difference between two slices of structs in Go?
Structs are only comparable with == if every field is itself comparable (no slices, maps, or funcs as fields). If that holds, map[MyStruct]struct{} works directly; otherwise, extract a comparable key field into the map instead of using the whole struct, or implement your own equality check with an explicit loop.
Is a nested loop ever an acceptable way to diff two slices in Go?
For small slices - a few dozen elements or fewer, or one-off scripts where clarity matters more than speed - a nested loop with slices.Contains is perfectly readable and the O(n * m) cost is negligible. It becomes a real problem once either slice reaches the thousands.

Share this tool

Featured on ToolDirs
© 2026 ListDiff

Free online list and text processing tools.