我正在尝试找到一个解决方案来检查2个切片中的相等性。不幸的是,我发现的答案要求切片中的值处于相同的顺序。例如,http://play.golang.org/p/yV0q1_u3xR将相等性评估为false
我想要一个让[]string{"a","b","c"} == []string{"b","a","c"}
评估为true
的解决方案
更多示例
[]string{"a","a","c"} == []string{"c","a","c"}
>>> false
[]string{"z","z","x"} == []string{"x","z","z"}
>>> true
答案 0 :(得分:6)
这是一个替代解决方案,虽然可能有点冗长:
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)
}
}
if len(diff) == 0 {
return true
}
return false
}
上试试
答案 1 :(得分:5)
我认为最简单的方法是将每个数组/切片中的元素映射到它们的出现次数,然后比较这些地图:
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
}
如果您的数组/切片包含不同类型或不同长度的元素,您需要添加一些额外的尽职调查,例如短路。
答案 2 :(得分:1)
其他答案有更好的时间复杂度O(N)
vs (O(N log(N))
,这在我的回答中,如果切片中的元素经常重复,我的解决方案也将占用更多内存,但我想添加因为我认为这是最直接的方式:
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))
}
结果:
false
true
答案 3 :(得分:0)
我知道已经回答了,但是我仍然想补充一下我的答案。通过在此处stretchr/testify遵循以下代码,我们可以得到类似的
func Elementsmatch(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
if aLen != bLen {
return fmt.Sprintf("Len of the lists don't match , len listA %v, len listB %v", aLen, bLen), false
}
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
found := false
element := listA[i]
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if element == listB[j] {
visited[j] = true
found = true
break
}
}
if !found {
return fmt.Sprintf("element %s appears more times in %s than in %s", element, listA, listB), false
}
}
return "", true
}
现在让我们来讨论一下与基于地图的解决方案相比,该解决方案的性能。好吧,这实际上取决于要比较的列表的大小。如果列表的大小很大(我想说大于20),则使用map方法更好,否则就足够了。
在Go PlayGround上它总是显示0,但是在本地系统上运行它,您会看到随着列表大小的增加所花费的时间差异
所以我建议的解决方案是在solution
上方添加基于地图的比较func Elementsmatch(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
if aLen != bLen {
return fmt.Sprintf("Len of the lists don't match , len listA %v, len listB %v", aLen, bLen), false
}
if aLen > 20 {
return elementsMatchByMap(listA, listB)
}else{
return elementsMatchByLoop(listA, listB)
}
}
func elementsMatchByLoop(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
found := false
element := listA[i]
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if element == listB[j] {
visited[j] = true
found = true
break
}
}
if !found {
return fmt.Sprintf("element %s appears more times in %s than in %s", element, listA, listB), false
}
}
return "", true
}
func elementsMatchByMap(x, y []string) (string, bool) {
// 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 fmt.Sprintf(" %v is not present in list b", _y), false
}
diff[_y] -= 1
if diff[_y] == 0 {
delete(diff, _y)
}
}
if len(diff) == 0 {
return "", true
}
return "", false
}
答案 4 :(得分:0)
概括testify ElementsMatch的代码,比较任何种类的对象的解决方案(在示例[]map[string]string
中):
答案 5 :(得分:0)
由于我没有足够的声誉来发表评论,因此我不得不发布另一个答案,其代码可读性更好:
func AssertSameStringSlice(x, y []string) bool {
if len(x) != len(y) {
return false
}
itemAppearsTimes := make(map[string]int, len(x))
for _, i := range x {
itemAppearsTimes[i]++
}
for _, i := range y {
if _, ok := itemAppearsTimes[i]; !ok {
return false
}
itemAppearsTimes[i]--
if itemAppearsTimes[i] == 0 {
delete(itemAppearsTimes, i)
}
}
if len(itemAppearsTimes) == 0 {
return true
}
return false
}
逻辑与此answer
中的逻辑相同答案 6 :(得分:0)
就像 adrianlzt 在他的 answer 中所写的那样,可以使用 testify 中的 install.packages("survival")
install.packages("lattice")
install.packages("ggplot2")
install.packages("Hmisc")
实现来实现这一点。但是,当您只需要比较的布尔结果时,如何重用实际的 testify 模块而不是复制该代码? testify 中的实现用于测试代码,通常采用 assert.ElementsMatch
参数。
事实证明 ElementsMatch 可以很容易地在测试代码之外使用。所需要的只是一个带有 testing.T
方法的接口的虚拟实现:
ErrorF
或者在 The Go Playground 上进行测试,这是我改编自 adrianlzt 的示例。
答案 7 :(得分:0)
您可以将 cmp.Diff
与 cmpopts.SortSlices
一起使用:
less := func(a, b string) bool { return a < b }
equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""
以下是在 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)
}