Goで2つのスライスを比較し、スライスYにないスライスXの要素を取得する方法はありますか?その逆もありますか?
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
このようなものが機能するはずです:
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
}
順序が重要でなく、セットが大きい場合は、セット実装を使用し、そのdiff関数を使用してそれらを比較する必要があります。
セットは標準ライブラリの一部ではありませんが、たとえばこのライブラリを使用できます。たとえば、スライスからセットを自動的に初期化できます。 https://github.com/deckarep/golang-set
このようなもの:
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)
}
提供されたすべてのソリューションは、尋ねられた質問に正確に答えることができません。スライスの違いの代わりに、ソリューションはスライス内の要素のセットの違いを提供します。
具体的には、次の意図された例の代わりに:
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}
提供されるソリューションの結果は次のとおりです。
result1 := []int{10,13}
result2 := []int{14,15}
厳密に例の結果を生成するには、別の方法が必要です。ここに2つの解決策があります:
スライスがすでにソートされている場合:
スライスを並べ替えてから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) { //All remaining in X are greater than Y, just copy over
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
}
マップを使用した未ソートバージョン
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
}
編集:2つのバージョンをテストするためのベンチマークを作成しました(マップにゼロ値を削除するわずかな変更が加えられています)。 Timeが正しく機能しないため、Go Playgroundで実行されないため、自分のコンピューターで実行しました。
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
ご覧のとおり、マップを使用せずに配列を並べ替える場合ははるかに高速ですが、マップを使用すると、並べ替えてから反復アプローチを使用するよりも高速になります。小さなケースの場合、並べ替えは使用するのに十分な速さである可能性がありますが、ベンチマークはすぐに終了してタイミングが調整されます。
セットの実装をドラッグするのはやり過ぎかもしれません。 これで十分です :
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
}
スライスが非常に大きくなった場合は、ブルームフィルターを追加できます。
github.com/mb0/diff パッケージ( godoc.orgのドキュメント )もあります:
パッケージdiffは差分アルゴリズムを実装します。アルゴリズムは "An O(ND)差分アルゴリズムとそのバリエーション" 、Eugene Myers、AlgorithmicaVol。1No。2、1986、pp。251で説明されています。 -266。