Contiene método para una rebanada

Respuestas:

226

Mostafa ya ha señalado que dicho método es trivial de escribir, y mkb le dio una pista para usar la búsqueda binaria del paquete de clasificación. Pero si va a hacer una gran cantidad de tales controles, también puede considerar usar un mapa.

Es trivial verificar si existe una clave de mapa específica usando el value, ok := yourmap[key]idioma. Como no está interesado en el valor, también puede crear un map[string]struct{}por ejemplo. Usar un struct{}espacio vacío aquí tiene la ventaja de que no requiere espacio adicional y el tipo de mapa interno de Go está optimizado para ese tipo de valores. Por lo tanto, map[string] struct{}es una opción popular para sets en el mundo Go.

tux21b
fuente
27
También tenga en cuenta que debe escribir struct{}{}para obtener el valor de la estructura vacía para poder pasarla a su mapa cuando desee agregar un elemento. Simplemente pruébelo y, si encuentra algún problema, no dude en preguntar. También puede usar la solución de Mostafa si le resulta más fácil de entender (a menos que tenga grandes cantidades de datos).
tux21b
55
La solución es simple, eso es cierto. ¿Pero qué se necesita para agregar una funcionalidad tan básica al tiempo de ejecución? No he encontrado tales problemas en Go repo en github. Eso es triste y extraño.
Igor Petrov
1
¿Cómo se map[string] boolcompara con map[string] struct{}. map[string] struct{}parece un truco especialmente inicializando una estructura vacíastruct {}{}
vadasambar
@IgorPetrov estuvo de acuerdo, me sorprende que una característica tan básica no esté en el tiempo de ejecución.
jcollum
180

No, dicho método no existe, pero es trivial de escribir:

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

Puede usar un mapa si esa búsqueda es una parte importante de su código, pero los mapas también han costado.

Mostafa
fuente
258
En realidad, no es trivial, porque tiene que escribir uno para cada tipo que usa, y debido a que no hay sobrecarga, debe nombrar cada función de manera diferente, como en C. append () puede funcionar genéricamente porque tiene soporte especial de tiempo de ejecución. Un contenido genérico sería útil por la misma razón, pero en realidad la solución genérica es solo el soporte genérico en el idioma.
Eloff
15
@Eloffinterface{}
Alex Lockwood
2
@Alex Lockwood, ¿esto realmente funcionará con interfaces?
Ory Band
101
trivial == 7 líneas de código incluyendo 1 bucle 1 rama si declaración y 1 comparación? Creo que me estoy perdiendo algo aquí ...
tothemario
3
Pero, ¿por qué no agregar estos en Go Core?
Luna Lovegood
16

Si el segmento está ordenado, hay una búsqueda binaria implementada en el sortpaquete .

mkb
fuente
12

En lugar de usar a slice, mappuede ser una mejor solución.

ejemplo 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

holys
fuente
34
En su forma actual, este código no ofrece ningún beneficio, ya que no tiene sentido construir un mapa a partir de un segmento si solo lo va a usar una vez. - Para ser útil, este código debería proporcionar una función sliceToMapque haga toda la preparación. Después de eso, consultar el mapa es trivial y eficiente.
Roland Illig
9

El paquete de clasificación proporciona los bloques de construcción si su segmento está ordenado o si está dispuesto a ordenarlo.

input := []string{"bird", "apple", "ocean", "fork", "anchor"}
sort.Strings(input)

fmt.Println(contains(input, "apple")) // true
fmt.Println(contains(input, "grow"))  // false

...

func contains(s []string, searchterm string) bool {
    i := sort.SearchStrings(s, searchterm)
    return i < len(s) && s[i] == searchterm
}

SearchStringpromete regresar the index to insert x if x is not present (it could be len(a)), por lo que una comprobación de eso revela si la cadena está contenida en el segmento ordenado.

Henrik Aasted Sørensen
fuente
En términos de tiempo, la búsqueda regular es O(n)y esta solución lo hace O(n*log(n)).
plesiv
@plesiv es una búsqueda binaria, AFAICS. ¿No sería eso O (log n)?
Henrik Aasted Sørensen
sí, binary-search y la función containsson O(log(n)), pero el enfoque general se O(n*log(n))debe al tipo.
plesiv
3

Puede utilizar la reflejan paquete para iterar sobre una interfaz cuyo tipo concreto es una rebanada:

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

Ethan Kennedy
fuente
3
Claro que puedes usar el paquete reflect, pero solo porque puedas, no significa que debas hacerlo. La reflexión es muy cara.
Justin Ohms
3

Si no es factible utilizar un mapa para buscar elementos basados ​​en una clave, puede considerar la herramienta de guía . Goderive genera una implementación específica de tipo de un método contiene, haciendo que su código sea legible y eficiente.

Ejemplo;

type Foo struct {
    Field1 string
    Field2 int
} 

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

Para generar el método deriveContainsFoo:

  • Instalar goderive con go get -u github.com/awalterschulze/goderive
  • Ejecutar goderive ./...en su carpeta de espacio de trabajo

Este método se generará para deriveContains:

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

Goderive tiene soporte para otros métodos auxiliares útiles para aplicar un estilo de programación funcional en go.

Alexander van Trijffel
fuente
2
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
}
Jim Hsiang
fuente
2

No estoy seguro de que se necesiten genéricos aquí. Solo necesita un contrato para su comportamiento deseado. Hacer lo siguiente no es más que lo que tendría que hacer en otros idiomas si quisiera que sus propios objetos se comporten en colecciones, anulando Equals () y GetHashCode (), por ejemplo.

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
}
JonPen
fuente
1
"no es más de lo que tendrías que hacer en otros lenguajes" no es realmente cierto, por ejemplo, en C # Contains()está implementado List<T>, por lo que solo tienes que implementarlo Equals()para ese trabajo.
George
1

Creé un punto de referencia muy simple con las soluciones de estas respuestas.

https://gist.github.com/NorbertFenk/7bed6760198800207e84f141c41d93c7

No es un punto de referencia real porque inicialmente, no he insertado demasiados elementos, pero no dude en bifurcarlo y cambiarlo.

F. Norbert
fuente
Lo pensé pero no es tan representativo debido al hecho de que mi máquina no es tan potente.
F. Norbert
-1

El estilo de ir:

func Contains(n int, match func(i int) bool) bool {
    for i := 0; i < n; i++ {
        if match(i) {
            return true
        }
    }
    return false
}


s := []string{"a", "b", "c", "o"}
// test if s contains "o"
ok := Contains(len(s), func(i int) bool {
    return s[i] == "o"
})
Roy O'Young
fuente
2
Esto no responde a la pregunta, ni proporciona información adicional.
Croolman
-1

Puede considerarse un poco 'hacky', pero dependiendo del tamaño y el contenido del segmento, puede unir el segmento y hacer una búsqueda de cadena.

Por ejemplo, tiene un segmento que contiene valores de una sola palabra (por ejemplo, "sí", "no", "tal vez"). Estos resultados se agregan a un segmento. Si desea verificar si esta porción contiene algún resultado "quizás", puede usar

exSlice := ["yes", "no", "yes", "maybe"]
if strings.Contains(strings.Join(exSlice, ","), "maybe") {
  fmt.Println("We have a maybe!")
}

Lo adecuado que esto sea realmente depende del tamaño del corte y la longitud de sus miembros. Puede haber problemas de rendimiento o idoneidad para cortes grandes o valores largos, pero para cortes más pequeños de tamaño finito y valores simples, es una línea válida para lograr el resultado deseado.

chopper24
fuente
No funcionará para situaciones en las que los elementos tienen texto similar pero no exactamente el mismoexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal
Este es un enfoque bastante agradable para lograr una solución rápida y sucia. Solo necesita requerir un delimitador inequívoco (podría ser una coma) y hacer el trabajo adicional para poner entre corchetes ambas cadenas: ","+strings.Join(exSlice,",")+","y",maybe,"
nobar