比较两个切片。

huangapple go评论76阅读模式
英文:

Compare two slices

问题

在Go语言中,可以比较两个切片并获取在切片X中但不在切片Y中的元素,反之亦然。以下是示例代码:

package main

import (
	"fmt"
)

func compare(X, Y []int) []int {
	var result []int

	// 检查X中的元素是否在Y中
	for _, x := range X {
		found := false
		for _, y := range Y {
			if x == y {
				found = true
				break
			}
		}
		if !found {
			result = append(result, x)
		}
	}

	return result
}

func main() {
	X := []int{10, 12, 12, 12, 13}
	Y := []int{12, 14, 15}

	result1 := compare(X, Y)
	fmt.Println("Result1:", result1) // 如果你想找到在切片X中但不在切片Y中的元素

	result2 := compare(Y, X)
	fmt.Println("Result2:", result2) // 如果你想找到在切片Y中但不在切片X中的元素
}

以上代码定义了一个名为compare的函数,该函数接受两个整数切片X和Y作为参数,并返回一个整数切片,其中包含在X中但不在Y中的元素。在main函数中,我们调用了compare函数两次,分别传入X和Y,以及Y和X作为参数,并打印了结果。

英文:

Is there a way in Go to compare two slices and get the elements in slice X that are not in slice Y and vice versa?

    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

func compare(X, Y []int)  
 
calling compare(X, Y)   
    result1 := []int{10, 12, 12, 13} // if you're looking for elements in slice X that are not in slice Y

calling compare(Y, X)
    result2 := []int{14, 15} // if you're looking for elements in slice Y that are not in slice X

答案1

得分: 8

如果顺序不重要且集合很大,你应该使用一个集合实现,并使用其差集函数进行比较。

集合不是标准库的一部分,但你可以使用这个库,例如,你可以使用它从切片自动初始化一个集合。https://github.com/deckarep/golang-set

类似这样:

import (
	set "github.com/deckarep/golang-set"
	"fmt"
)

func main() {
	//注意集合接受 []interface{}
	X := []interface{}{10, 12, 12, 12, 13}
	Y := []interface{}{12, 14, 15}

	Sx := set.NewSetFromSlice(X)
	Sy := set.NewSetFromSlice(Y)
	result1 := Sx.Difference(Sy)
	result2 := Sy.Difference(Sx)

	fmt.Println(result1)
	fmt.Println(result2)
}
英文:

If order is not important, and the sets are large, you should use a set implementation, and use its diff function to compare them.

Sets are not part of the standard library, but you can use this library for example, you can initialize a set automatically from a slice with it. https://github.com/deckarep/golang-set

Something like this:

import (
	set "github.com/deckarep/golang-set"
	"fmt"
	)

func main() {
	//note that the set accepts []interface{}
	X := []interface{}{10, 12, 12, 12, 13}
	Y := []interface{}{12, 14, 15}

	Sx := set.NewSetFromSlice(X)
	Sy := set.NewSetFromSlice(Y)
	result1 := Sx.Difference(Sy)
	result2 := Sy.Difference(Sx)

	fmt.Println(result1)
	fmt.Println(result2)
}

答案2

得分: 4

所有提供的解决方案都未能准确回答所提出的问题。这些解决方案提供的是切片中元素集合的差异,而不是切片之间的差异。

具体而言,与预期的示例相比:

    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

func compare(X, Y []int)  

调用 compare(X, Y)   
    result1 := []int{10, 12, 12, 13} // 如果你想找到在切片X中而不在切片Y中的元素

调用 compare(Y, X)
    result2 := []int{14, 15}

提供的解决方案将导致:

result1 := []int{10,13}
result2 := []int{14,15}

要严格得到示例结果,需要使用不同的方法。以下是两种解决方案:

如果切片已经排序:

如果你对切片进行排序,然后调用 compare,这个解决方案可能会更快。如果你的切片已经排序,那么它肯定会更快。

func compare(X, Y []int) []int {
    difference := make([]int, 0)
    var i, j int
    for i < len(X) && j < len(Y) {
        if X[i] < Y[j] {
            difference = append(difference, X[i])
            i++
        } else if X[i] > Y[j] {
            j++
        } else { //X[i] == Y[j]
            j++
            i++
        }
    }
    if i < len(X) { //X中剩余的元素都大于Y,直接复制过来
        finalLength := len(X) - i + len(difference)
        if finalLength > cap(difference) {
            newDifference := make([]int, finalLength)
            copy(newDifference, difference)
            copy(newDifference[len(difference):], X[i:])
            difference = newDifference
        } else {
            differenceLen := len(difference)
            difference = difference[:finalLength]
            copy(difference[differenceLen:], X[i:])
        }
    }
    return difference
}

Go Playground 版本

使用映射的未排序版本

func compareMapAlternate(X, Y []int) []int {
    counts := make(map[int]int)
    var total int
    for _, val := range X {
        counts[val] += 1
        total += 1
    }
    for _, val := range Y {
        if count := counts[val]; count > 0 {
            counts[val] -= 1
            total -= 1
        }
    }
    difference := make([]int, total)
    i := 0
    for val, count := range counts {
        for j := 0; j < count; j++ {
            difference[i] = val
            i++
        }
    }
    return difference
}

Go Playground 版本

**编辑:**我为我的两个版本创建了一个基准测试(其中映射版本稍作修改,删除了映射中的零值)。它在 Go Playground 上无法运行,因为 Time 在其中无法正常工作,所以我在自己的计算机上运行了它。

compareSort 对切片进行排序,并调用 compare 的迭代版本,compareSorted 在 compareSort 之后运行,但依赖于切片已经排序。

Case: len(X)== 10000 && len(Y)== 10000
--compareMap time: 4.0024ms
--compareMapAlternate time: 3.0225ms
--compareSort time: 4.9846ms
--compareSorted time: 1ms
--Result length == 6754 6754 6754 6754
Case: len(X)== 1000000 && len(Y)== 1000000
--compareMap time: 378.2492ms
--compareMapAlternate time: 387.2955ms
--compareSort time: 816.5619ms
--compareSorted time: 28.0432ms
--Result length == 673505 673505 673505 673505
Case: len(X)== 10000 && len(Y)== 1000000
--compareMap time: 35.0269ms
--compareMapAlternate time: 43.0492ms
--compareSort time: 385.2629ms
--compareSorted time: 3.0242ms
--Result length == 3747 3747 3747 3747
Case: len(X)== 1000000 && len(Y)== 10000
--compareMap time: 247.1561ms
--compareMapAlternate time: 240.1727ms
--compareSort time: 400.2875ms
--compareSorted time: 17.0311ms
--Result length == 993778 993778 993778 993778

如你所见,如果数组已排序,则不使用映射的方法要快得多,但使用映射要比先对其进行排序,然后使用迭代方法要快。对于小规模的情况,排序可能足够快,应该使用它,但基准测试完成得太快,无法计时。

英文:

All of the solutions provided fail to precisely answer the question asked. Instead of the differences in the slices, the solutions provide the differences of the sets of elements in the slices.

Specifically, instead of the intended example of:

    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

func compare(X, Y []int)  

calling compare(X, Y)   
    result1 := []int{10, 12, 12, 13} // if you&#39;re looking for elements in slice X that are not in slice Y

calling compare(Y, X)
    result2 := []int{14, 15}

The provided solutions will result in:

result1 := []int{10,13}
result2 := []int{14,15}

To yield strictly the example result, a different method is required. Here are two solutions:

If the slices are already sorted:

This solution may be faster if you sort your slices, and then call compare. It'll definitely be faster if your slices are already sorted.

func compare(X, Y []int) []int {
	difference := make([]int, 0)
	var i, j int
	for i &lt; len(X) &amp;&amp; j &lt; len(Y) {
		if X[i] &lt; Y[j] {
			difference = append(difference, X[i])
			i++
		} else if X[i] &gt; Y[j] {
			j++
		} else { //X[i] == Y[j]
			j++
			i++
		}
	}
	if i &lt; len(X) { //All remaining in X are greater than Y, just copy over
		finalLength := len(X) - i + len(difference)
		if finalLength &gt; cap(difference) {
			newDifference := make([]int, finalLength)
			copy(newDifference, difference)
			copy(newDifference[len(difference):], X[i:])
			difference = newDifference
		} else {
			differenceLen := len(difference)
			difference = difference[:finalLength]
			copy(difference[differenceLen:], X[i:])
		}
	}
	return difference
}

Go Playground version

Unsorted Version using maps

func compareMapAlternate(X, Y []int) []int {
	counts := make(map[int]int)
	var total int
	for _, val := range X {
		counts[val] += 1
		total += 1
	}
	for _, val := range Y {
		if count := counts[val]; count &gt; 0 {
			counts[val] -= 1
			total -= 1
		}
	}
	difference := make([]int, total)
	i := 0
	for val, count := range counts {
		for j := 0; j &lt; count; j++ {
			difference[i] = val
			i++
		}
	}
	return difference
}

Go Playground version

Edit: I've created a benchmark for testing my two version (with the map having a slight modification where it deletes zero values from the map). It won't run on Go Playground because Time doesn't work properly on it, so I ran it on my own computer.

compareSort sorts the slice and calls the iterated version of compare, and compareSorted runs afters compareSort but relies on the slice already being sorted.

Case: len(X)== 10000 &amp;&amp; len(Y)== 10000
--compareMap time: 4.0024ms
--compareMapAlternate time: 3.0225ms
--compareSort time: 4.9846ms
--compareSorted time: 1ms
--Result length == 6754 6754 6754 6754
Case: len(X)== 1000000 &amp;&amp; len(Y)== 1000000
--compareMap time: 378.2492ms
--compareMapAlternate time: 387.2955ms
--compareSort time: 816.5619ms
--compareSorted time: 28.0432ms
--Result length == 673505 673505 673505 673505
Case: len(X)== 10000 &amp;&amp; len(Y)== 1000000
--compareMap time: 35.0269ms
--compareMapAlternate time: 43.0492ms
--compareSort time: 385.2629ms
--compareSorted time: 3.0242ms
--Result length == 3747 3747 3747 3747
Case: len(X)== 1000000 &amp;&amp; len(Y)== 10000
--compareMap time: 247.1561ms
--compareMapAlternate time: 240.1727ms
--compareSort time: 400.2875ms
--compareSorted time: 17.0311ms
--Result length == 993778 993778 993778 993778

As you can see, if the array is sorted not using maps is much faster, but using maps is faster than sorting it and then using the iterated approach. For small cases sorting may be quick enough that it should be used, but the benchmark would finish to quickly to be timed.

答案3

得分: 3

以下是翻译好的代码:

package main

import "fmt"

func main() {
    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

    fmt.Println(compare(X, Y))
    fmt.Println(compare(Y, X))
}

func compare(X, Y []int) []int {
    m := make(map[int]int)

    for _, y := range Y {
        m[y]++
    }

    var ret []int
    for _, x := range X {
        if m[x] > 0 {
            m[x]--
            continue
        }
        ret = append(ret, x)
    }

    return ret
}

你可以在这里查看代码:http://play.golang.org/p/4DujR2staI

英文:

Something like this should work:

package main

import &quot;fmt&quot;

func main() {
    X := []int{10, 12, 12, 12, 13}
    Y := []int{12, 14, 15}

    fmt.Println(compare(X, Y))
    fmt.Println(compare(Y, X))
}

func compare(X, Y []int) []int {
    m := make(map[int]int)

    for _, y := range Y {
        m[y]++
    }

    var ret []int
    for _, x := range X {
        if m[x] &gt; 0 {
            m[x]--
            continue
        }
        ret = append(ret, x)
    }

    return ret
}

<http://play.golang.org/p/4DujR2staI>

答案4

得分: 0

如果您不想重新发明轮子(然后维护它)来使用自定义算法,可以使用github.com/r3labs/diff/v2

diff函数返回一个更改日志,您可以检查该日志以找到确切的更改,以create/update/delete条目的形式。该更改日志也可以转换为JSON格式:

package main

import (
	"encoding/json"
	"github.com/r3labs/diff/v2"
	"os"
)

func main() {
	x := []int{10, 12, 12, 12, 13}
	y := []int{12, 14, 15}
	ch, _ := diff.Diff(x, y)
	json.NewEncoder(os.Stdout).Encode(ch)
}

它会打印出更改日志:

[{"type":"delete","path":["0"],"from":10,"to":null},{"type":"update","path":["2"],"from":12,"to":15},{"type":"delete","path":["3"],"from":12,"to":null},{"type":"delete","path":["4"],"from":13,"to":null},{"type":"create","path":["1"],"from":null,"to":14}]

该更改日志基本上描述了从第一个切片获取第二个切片所需的操作。

该库还提供类似补丁的功能。您可以直接将更改日志传递给diff.Patch并将更改应用于目标值:

diff.Patch(ch, &x)
fmt.Println(x) // [12 14 15],与'y'切片相同

请注意,此差异器的默认行为是忽略切片元素的顺序。因此,如果您对比[]int{12, 14, 15}[]int{12, 15, 14},它将生成一个空的更改日志。

要考虑项目的顺序,可以使用选项diff.SliceOrdering(true)

func main() {
	x := []int{12, 15, 14}
	y := []int{12, 14, 15}
	ch, _ := diff.Diff(x, y, diff.SliceOrdering(true))
	json.NewEncoder(os.Stdout).Encode(ch)
    // [{"type":"update","path":["1"],"from":15,"to":14},{"type":"update","path":["2"],"from":14,"to":15}]
}
英文:

If you don't want to reinvent the wheel (and then maintain it) with custom algos, you can use github.com/r3labs/diff/v2.

The diff returns a changelog that you can inspect to find exactly what changed, in the form of create/update/delete entries. The changelog can also be marshalled to JSON:

package main

import (
	&quot;encoding/json&quot;
	&quot;github.com/r3labs/diff/v2&quot;
	&quot;os&quot;
)

func main() {
	x := []int{10, 12, 12, 12, 13}
	y := []int{12, 14, 15}
	ch, _ := diff.Diff(x, y)
	json.NewEncoder(os.Stdout).Encode(ch)
}

It prints the changelog:

[{&quot;type&quot;:&quot;delete&quot;,&quot;path&quot;:[&quot;0&quot;],&quot;from&quot;:10,&quot;to&quot;:null},{&quot;type&quot;:&quot;update&quot;,&quot;path&quot;:[&quot;2&quot;],&quot;from&quot;:12,&quot;to&quot;:15},{&quot;type&quot;:&quot;delete&quot;,&quot;path&quot;:[&quot;3&quot;],&quot;from&quot;:12,&quot;to&quot;:null},{&quot;type&quot;:&quot;delete&quot;,&quot;path&quot;:[&quot;4&quot;],&quot;from&quot;:13,&quot;to&quot;:null},{&quot;type&quot;:&quot;create&quot;,&quot;path&quot;:[&quot;1&quot;],&quot;from&quot;:null,&quot;to&quot;:14}]

The changelog essentially describes the operations necessary to obtain the second slice from the first.

The library also provides patch-like functionality. You can pass the changelog right into diff.Patch and apply the changes to the target value:

	diff.Patch(ch, &amp;x)
	fmt.Println(x) // [12 14 15] just like the &#39;y&#39; slice

Note that the default behavior of this differ is to ignore slice element order. So if you diff []int{12, 14, 15} and []int{12, 15, 14} it will produce an empty changelog.

To account for item ordering, use the option diff.SliceOrdering(true):

func main() {
	x := []int{12, 15, 14}
	y := []int{12, 14, 15}
	ch, _ := diff.Diff(x, y, diff.SliceOrdering(true))
	json.NewEncoder(os.Stdout).Encode(ch)
    // [{&quot;type&quot;:&quot;update&quot;,&quot;path&quot;:[&quot;1&quot;],&quot;from&quot;:15,&quot;to&quot;:14},{&quot;type&quot;:&quot;update&quot;,&quot;path&quot;:[&quot;2&quot;],&quot;from&quot;:14,&quot;to&quot;:15}]

}

答案5

得分: -1

拖入一个集合实现可能有点过头了。这应该就足够了

func diff(X, Y []int) []int {

  diff := []int{}
  vals := map[int]struct{}{}

  for _, x := range X {
    vals[x] = struct{}{}
  }

  for _, x := range Y {
    if _, ok := vals[x]; !ok {
      diff = append(diff, x)
    }
  }

  return diff
}

如果切片变得非常大,可以添加一个布隆过滤器。

英文:

Dragging in a set implementation might be overkill. This should be enough:

func diff(X, Y []int) []int {

  diff := []int{}
  vals := map[int]struct{}{}

  for _, x := range X {
    vals[x] = struct{}{}
  }

  for _, x := range Y {
    if _, ok := vals[x]; !ok {
      diff = append(diff, x)
    }
  }

  return diff
}

A bloom filter can be added if the slices get really big.

答案6

得分: -1

目前我回答这个问题的时候,Go语言的版本是1.17.3。

我们可以使用内置的包:reflect

该包提供了一个非常有用的函数DeepEqual,符合你的要求。

reflect.DeepEqual(X, Y)
英文:

At the moment I answer this question, go version is 1.17.3

We can use a built-in package: reflect

The package provides a very useful function DeepEqual that fits your requirement

reflect.DeepEqual(X, Y)

huangapple
  • 本文由 发表于 2014年5月26日 20:23:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/23870102.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定