web-dev-qa-db-fra.com

Comparer deux tranches

Existe-t-il un moyen dans Go de comparer deux tranches et d’obtenir les éléments de la tranche X qui ne sont pas de la tranche Y et inversement?

    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

Quelque chose comme ça devrait marcher:

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

Si l'ordre n'est pas important et que les ensembles sont volumineux, vous devez utiliser une implémentation d'ensemble et utiliser sa fonction diff pour les comparer.

Les ensembles ne font pas partie de la bibliothèque standard, mais vous pouvez utiliser cette bibliothèque, par exemple, vous pouvez initialiser automatiquement un ensemble à partir d'une tranche. https://github.com/deckarep/golang-set

Quelque chose comme ça:

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

Toutes les solutions proposées ne répondent pas précisément à la question posée. Au lieu des différences dans les tranches, les solutions fournissent les différences des ensembles d'éléments dans les tranches.

Plus précisément, au lieu de l'exemple prévu de:

    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}

Les solutions fournies auront pour résultat:

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

Pour obtenir strictement l'exemple de résultat, une méthode différente est requise. Voici deux solutions:

Si les tranches sont déjà triées:

Cette solution peut être plus rapide si vous triez vos tranches, puis appelez la comparaison. Ce sera certainement plus rapide si vos tranches sont déjà triées.

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
}

Go Playground version

Version non triée à l'aide de cartes

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 version

Edit: J'ai créé un point de repère pour tester mes deux versions (la carte étant légèrement modifiée, elle supprime les valeurs nulles de la carte). Il ne fonctionnera pas sur Go Playground, car Time ne fonctionne pas correctement. Je l'ai donc exécuté sur mon propre ordinateur.

compareSort trie la tranche et appelle la version itérée de compare, et compareSorted exécute après comparSort mais s'appuie sur la tranche déjà triée.

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

Comme vous pouvez le constater, si le tableau est trié, l’utilisation des cartes n’est pas beaucoup plus rapide, mais l’utilisation des cartes est plus rapide que le tri puis l’utilisation de l’approche itérée. Pour les petites affaires, le tri peut être assez rapide pour être utilisé, mais le repère finirait trop vite pour être chronométré.

3
Laremere

Glisser dans une mise en œuvre peut être excessif. Cela devrait suffire :

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
}

Un filtre de bloom peut être ajouté si les tranches deviennent vraiment grandes.

0
Ilia Choly

Il y a aussi le github.com/mb0/diff package ( docs sur godoc.org ):

Le paquet diff implémente un algorithme de différence. L'algorithme est décrit dans "Un O(ND) Algorithme de différence et ses variantes" , Eugene Myers, Algorithmica Vol. 1 N ° 2, 1986, p. 251-266.

0
akavel