web-dev-qa-db-fra.com

Contient la méthode pour une tranche

Existe-t-il une méthode similaire à une méthode slice.contains(object) dans Go sans avoir à effectuer une recherche dans chaque élément d'une tranche?

152
vosmith

Mostafa a déjà fait remarquer qu'une telle méthode est facile à écrire et mkb vous a donné un indice pour utiliser la recherche binaire du paquet de tri. Mais si vous allez effectuer beaucoup de vérifications de ce type, vous pouvez également utiliser une carte.

Il est facile de vérifier si une clé de carte spécifique existe en utilisant l'idiome value, ok := yourmap[key]. Puisque la valeur ne vous intéresse pas, vous pouvez également créer un map[string]struct{} par exemple. L'utilisation d'un struct{} vide a l'avantage de ne pas nécessiter d'espace supplémentaire et que le type de carte interne de Go est optimisé pour ce type de valeurs. Par conséquent, map[string] struct{} est un choix populaire pour les ensembles du monde de Go.

190
tux21b

Non, une telle méthode n'existe pas, mais il est facile d'écrire:

func contains(s []int, e int) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

Vous pouvez utiliser une carte si cette recherche constitue une partie importante de votre code, mais les cartes ont également un coût.

142
Mostafa

Si la tranche est triée, une recherche binaire est implémentée dans le package sort .

13
mkb

Au lieu d'utiliser un slice, map peut être une meilleure solution.

exemple simple:

package main

import "fmt"


func contains(slice []string, item string) bool {
    set := make(map[string]struct{}, len(slice))
    for _, s := range slice {
        set[s] = struct{}{}
    }

    _, ok := set[item] 
    return ok
}

func main() {

    s := []string{"a", "b"}
    s1 := "a"
    fmt.Println(contains(s, s1))

}

http://play.golang.org/p/CEG6cu4JTf

8
holys

Vous pouvez utiliser le package reflect pour parcourir une interface dont le type concret est une tranche:

func HasElem(s interface{}, elem interface{}) bool {
    arrV := reflect.ValueOf(s)

    if arrV.Kind() == reflect.Slice {
        for i := 0; i < arrV.Len(); i++ {

            // XXX - panics if slice element points to an unexported struct field
            // see https://golang.org/pkg/reflect/#Value.Interface
            if arrV.Index(i).Interface() == elem {
                return true
            }
        }
    }

    return false
}

https://play.golang.org/p/jL5UD7yCNq

5
Ethan Kennedy

Pas sûr que les génériques soient nécessaires ici. Vous avez juste besoin d'un contrat pour votre comportement souhaité. Ce qui suit n’est rien de plus que ce que vous auriez à faire dans d’autres langues si vous vouliez que vos propres objets se comportent dans des collections, en surchargeant par exemple Equals () et GetHashCode ().

type Identifiable interface{
    GetIdentity() string
}

func IsIdentical(this Identifiable, that Identifiable) bool{
    return (&this == &that) || (this.GetIdentity() == that.GetIdentity())
}

func contains(s []Identifiable, e Identifiable) bool {
    for _, a := range s {
        if IsIdentical(a,e) {
            return true
        }
    }
    return false
}
3
JonPen

S'il n'est pas possible d'utiliser une carte pour rechercher des éléments en fonction d'une clé, vous pouvez utiliser l'outil goderive . Goderive génère une implémentation spécifique à un type de la méthode contient, rendant votre code à la fois lisible et efficace.

Exemple;

type Foo struct {
    Field1 string
    Field2 int
} 

func Test(m Foo) bool {
     var allItems []Foo
     return deriveContainsFoo(allItems, m)
}

Pour générer la méthode deriveContainsFoo:

  • Installez goderive avec go get -u github.com/awalterschulze/goderive
  • Exécutez goderive ./... dans votre dossier d’espace de travail.

Cette méthode sera générée pour deriveContains:

func deriveContainsFoo(list []Foo, item Foo) bool {
    for _, v := range list {
        if v == item {
            return true
        }
    }
    return false
}

Goderive supporte pas mal d’autres méthodes utiles pour appliquer un style de programmation fonctionnel à la volée.

func Contain(target interface{}, list interface{}) (bool, int) {
    if reflect.TypeOf(list).Kind() == reflect.Slice || reflect.TypeOf(list).Kind() == reflect.Array {
        listvalue := reflect.ValueOf(list)
        for i := 0; i < listvalue.Len(); i++ {
            if target == listvalue.Index(i).Interface() {
                return true, i
            }
        }
    }
    if reflect.TypeOf(target).Kind() == reflect.String && reflect.TypeOf(list).Kind() == reflect.String {
        return strings.Contains(list.(string), target.(string)), strings.Index(list.(string), target.(string))
    }
    return false, -1
}
2
Jim Hsiang