Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

check for equality on slices without order

Tags:

equality

slice

go

I am trying to find a solution to check for equality in 2 slices. Unfortanely, the answers I have found require values in the slice to be in the same order. For example, http://play.golang.org/p/yV0q1_u3xR evaluates equality to false.
I want a solution that lets []string{"a","b","c"} == []string{"b","a","c"} evaluate to true.
MORE EXAMPLES
[]string{"a","a","c"} == []string{"c","a","c"} >>> false
[]string{"z","z","x"} == []string{"x","z","z"} >>> true

like image 759
XXX Avatar asked Mar 15 '16 00:03

XXX


People also ask

How can I check if two slices are equal?

Basic case In most cases, you will want to write your own code to compare the elements of two slices. For arrays, however, you can use the comparison operators == and !=

Is it possible to check if the slices are equal with == operator or not?

Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal. Other values - numbers, bools, strings, and channels - are deeply equal if they are equal using Go's == operator.

Are Golang slices ordered?

In Go, arrays and slices are data structures that consist of an ordered sequence of elements.

How do I compare byte arrays in go?

In the Go slice, you are allowed to compare two slices of the byte type with each other using Compare() function. This function returns an integer value which represents that these slices are equal or not and the values are: If the result is 0, then slice_1 == slice_2. If the result is -1, then slice_1 < slice_2.


4 Answers

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

like image 149
sberry Avatar answered Nov 11 '22 15:11

sberry


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
like image 34
Akavall Avatar answered Nov 11 '22 15:11

Akavall


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"

}
like image 35
Johan Wikström Avatar answered Nov 11 '22 15:11

Johan Wikström


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.

like image 33
Dimitar Dimitrov Avatar answered Nov 11 '22 14:11

Dimitar Dimitrov