web-dev-qa-db-ja.com

2つのスライスを比較する

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
12
jwesonga

このようなものが機能するはずです:

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

2
ctn

順序が重要でなく、セットが大きい場合は、セット実装を使用し、その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)
}
7
Not_a_Golfer

提供されたすべてのソリューションは、尋ねられた質問に正確に答えることができません。スライスの違いの代わりに、ソリューションはスライス内の要素のセットの違いを提供します。

具体的には、次の意図された例の代わりに:

    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
}

Go Playgroundバージョン

編集: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

ご覧のとおり、マップを使用せずに配列を並べ替える場合ははるかに高速ですが、マップを使用すると、並べ替えてから反復アプローチを使用するよりも高速になります。小さなケースの場合、並べ替えは使用するのに十分な速さである可能性がありますが、ベンチマークはすぐに終了してタイミングが調整されます。

3
Laremere

セットの実装をドラッグするのはやり過ぎかもしれません。 これで十分です

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
}

スライスが非常に大きくなった場合は、ブルームフィルターを追加できます。

0
Ilia Choly

github.com/mb0/diff パッケージ( godoc.orgのドキュメント )もあります:

パッケージdiffは差分アルゴリズムを実装します。アルゴリズムは "An O(ND)差分アルゴリズムとそのバリエーション" 、Eugene Myers、AlgorithmicaVol。1No。2、1986、pp。251で説明されています。 -266。

0
akavel