Contém método para uma fatia


Respostas:


226

Mostafa já apontou que esse método é trivial de escrever, e o mkb deu uma dica para você usar a pesquisa binária no pacote de classificação. Mas se você fizer muitas dessas verificações, também poderá usar um mapa.

É trivial verificar se existe uma chave de mapa específica usando o value, ok := yourmap[key]idioma. Como você não está interessado no valor, você também pode criar um, map[string]struct{}por exemplo. Usar um vazio struct{}aqui tem a vantagem de não exigir espaço adicional e o tipo de mapa interno do Go é otimizado para esse tipo de valores. Portanto, map[string] struct{}é uma escolha popular para os sets no mundo Go.


27
Observe também que é necessário escrever struct{}{}para obter o valor da estrutura vazia, para que você possa passá-lo ao seu mapa quando desejar adicionar um elemento. Apenas tente e, se encontrar algum problema, não hesite em perguntar. Você também pode usar a solução da Mostafa se for mais fácil para você entender (a menos que você tenha grandes quantidades de dados).
tux21b

5
A solução é simples, é verdade. Mas o que é preciso para adicionar essa funcionalidade básica ao tempo de execução? Não encontrei esses problemas no repositório Go no github. Isso é triste e estranho.
Igor Petrov

1
Como se map[string] boolcompara com map[string] struct{}. map[string] struct{}parece ser um hack especialmente inicializar um struct vaziostruct {}{}
vadasambar

@IgorPetrov concordou, estou surpreso que um recurso tão básico ainda não esteja no tempo de execução.
jcollum 21/04

180

Não, esse método não existe, mas é trivial escrever:

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

Você pode usar um mapa se essa pesquisa for uma parte importante do seu código, mas os mapas também custam.


257
Na verdade, isso não é trivial, porque você precisa escrever um para cada tipo que usa e como não há sobrecarga, é necessário nomear cada função de maneira diferente, como em C. append () pode funcionar genericamente porque possui suporte especial ao tempo de execução. Um conteúdo genérico seria útil pelo mesmo motivo, mas realmente a solução genérica é apenas o suporte a genéricos no idioma.
Eloff

15
@Eloffinterface{}
Alex Lockwood

2
@ Alex Lockwood isso realmente funcionará com interfaces?
Ory Banda

101
trivial == 7 linhas de código incluindo 1 loop 1 ramificação if declaração e 1 comparação? Eu acho que estou faltando alguma coisa aqui ...
tothemario

3
Mas por que não adicioná-los no próprio núcleo?
Luna Lovegood


11

Em vez de usar a slice, mappode ser uma solução melhor.

exemplo simples:

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


34
Na sua forma atual, esse código não oferece nenhum benefício, pois não faz sentido construir um mapa a partir de uma fatia se você o usar apenas uma vez. - Para ser útil, esse código deve fornecer uma função sliceToMapque faça toda a preparação. Depois disso, consultar o mapa é trivial e eficiente.
Roland Illig 26/10/2015

9

O pacote de classificação fornece os blocos de construção se sua fatia é classificada ou você deseja classificá-la.

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 retornar the index to insert x if x is not present (it could be len(a)), portanto, uma verificação disso revela se a string está contida na fatia classificada.


Em termos de tempo, a pesquisa regular é O(n)e esta solução o faz O(n*log(n)).
plesiv

@plesiv é uma pesquisa binária, AFAICS. Isso não tornaria O (log n)?
Henrik Aasted Sørensen

sim, a pesquisa binária e a função containssão O(log(n)), mas a abordagem geral se O(n*log(n))deve ao tipo.
plesiv

3

Você pode usar o pacote reflect para iterar sobre uma interface cujo tipo concreto é uma fatia:

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


3
Claro que você pode usar o pacote de reflexão, mas apenas porque você pode, não significa que você deveria. A reflexão é muito cara.
Justin Ohms

3

Se não for possível usar um mapa para encontrar itens com base em uma chave, considere a ferramenta goderive . O Goderive gera uma implementação específica de tipo de um método contains, tornando seu código legível e eficiente.

Exemplo;

type Foo struct {
    Field1 string
    Field2 int
} 

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

Para gerar o método deriveContainsFoo:

  • Instale o goderive com go get -u github.com/awalterschulze/goderive
  • Executar goderive ./...na pasta da área de trabalho

Este método será gerado para deriveContains:

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

O Goderive tem suporte para alguns outros métodos auxiliares úteis para aplicar um estilo de programação funcional em movimento.


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
}

2

Não tenho certeza se os genéricos são necessários aqui. Você só precisa de um contrato para o comportamento desejado. Fazer o seguinte não é mais do que seria necessário em outros idiomas se você desejasse que seus próprios objetos se comportassem em coleções, substituindo Equals () e GetHashCode () por exemplo.

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
}

1
"não é mais do que o que você teria que fazer em outros idiomas" não é realmente verdade - por exemplo, o C # Contains()é implementado List<T>, então você só precisa implementar Equals()esse trabalho.
George


0

Pode ser considerado um pouco "hacky", mas dependendo do tamanho e do conteúdo da fatia, você pode unir a fatia e fazer uma pesquisa de string.

Por exemplo, você tem uma fatia contendo valores de palavra única (por exemplo, "sim", "não", "talvez"). Esses resultados são anexados a uma fatia. Se você quiser verificar se esta fatia contém algum resultado "talvez", use

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

A adequação disso realmente depende do tamanho da fatia e do comprimento de seus membros. Pode haver problemas de desempenho ou adequação para fatias grandes ou valores longos, mas para fatias menores de tamanho finito e valores simples, é uma linha única válida para alcançar o resultado desejado.


Não funcionará para situações em que elementos tenham texto semelhante, mas não exatamente o mesmoexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal

Essa é uma abordagem bastante agradável para obter uma solução de uma linha rápida e suja. Você só precisa exigir um delimitador inequívoco (pode ser uma vírgula) e fazer o trabalho extra para ","+strings.Join(exSlice,",")+","",maybe,"
agrupar as

-1

O estilo go:

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"
})

2
Isso não responde à pergunta nem fornece informações adicionais.
Croolman
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.