check for equality on slices without order

You can use cmp.Diff together with cmpopts.SortSlices:

less := func(a, b string) bool { return a < b }
equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""

Here is a full example that runs on the Go Playground:

package main

import (
    "fmt"

    "github.com/google/go-cmp/cmp"
    "github.com/google/go-cmp/cmp/cmpopts"
)

func main() {
    x := []string{"a", "b", "c"}
    y := []string{"a", "c", "b"}
    
    less := func(a, b string) bool { return a < b }
    equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""
    fmt.Println(equalIgnoreOrder) // prints "true"

}

I would think the easiest way would be to map the elements in each array/slice to their number of occurrences, then compare the maps:

func main() {
    x := []string{"a","b","c"}
    y := []string{"c","b","a"}

    xMap := make(map[string]int)
    yMap := make(map[string]int)

    for _, xElem := range x {
        xMap[xElem]++
    }
    for _, yElem := range y {
        yMap[yElem]++
    }

    for xMapKey, xMapVal := range xMap {
        if yMap[xMapKey] != xMapVal {
            return false
        }
    }
    return true
}

You'll need to add some additional due dilligence, like short circuiting if your arrays/slices contain elements of different types or are of different length.


The other answers have better time complexity O(N) vs (O(N log(N)), that are in my answer, also my solution will take up more memory if elements in the slices are repeated frequently, but I wanted to add it because I think this is the most straight forward way to do it:

package main

import (
    "fmt"
    "sort"
    "reflect"
)

func array_sorted_equal(a, b []string) bool {
    if len(a) != len(b) {return false }

    a_copy := make([]string, len(a))
    b_copy := make([]string, len(b))

    copy(a_copy, a)
    copy(b_copy, b)

    sort.Strings(a_copy)
    sort.Strings(b_copy)

    return reflect.DeepEqual(a_copy, b_copy)
}

func main() {
    a := []string {"a", "a", "c"}
    b := []string {"c", "a", "c"}
    c := []string {"z","z","x"} 
    d := []string {"x","z","z"}


    fmt.Println( array_sorted_equal(a, b))
    fmt.Println( array_sorted_equal(c, d))

}

Result:

false
true

Here is an alternate solution, though perhaps a bit verbose:

func sameStringSlice(x, y []string) bool {
    if len(x) != len(y) {
        return false
    }
    // create a map of string -> int
    diff := make(map[string]int, len(x))
    for _, _x := range x {
        // 0 value for int is 0, so just increment a counter for the string
        diff[_x]++
    }
    for _, _y := range y {
        // If the string _y is not in diff bail out early
        if _, ok := diff[_y]; !ok {
            return false
        }
        diff[_y] -= 1
        if diff[_y] == 0 {
            delete(diff, _y)
        }
    }
    return len(diff) == 0
}

Try it on the Go Playground

Tags:

Equality

Slice

Go