Містить метод для скибочки


Відповіді:


226

Мостафа вже вказував, що такий метод є тривіальним для написання, і mkb дав вам підказку щодо використання двійкового пошуку з пакету сортування. Але якщо ви збираєтеся робити багато таких містять перевірок, ви можете також замість цього використати карту.

Тривіально перевірити, чи існує конкретний ключ карти за допомогою value, ok := yourmap[key]ідіоми. Оскільки вас не цікавить значення, ви також можете створити, map[string]struct{}наприклад,. Використання порожнього struct{}тут має перевагу в тому, що він не потребує додаткового простору, а внутрішній тип карти Go оптимізований для такого типу значень. Тому map[string] struct{}є популярним вибором для наборів у світі Go.


27
Також зауважте, що вам потрібно написати, struct{}{}щоб отримати значення порожньої структури, щоб ви могли передати його на свою карту, коли хочете додати елемент. Просто спробуйте, і якщо у вас виникли якісь проблеми, сміливо запитайте. Ви також можете використовувати рішення Mostafa, якщо це простіше зрозуміти (якщо у вас немає величезної кількості даних).
tux21b

5
Рішення просте, це правда. Але що потрібно, щоб додати таку базову функціональність під час виконання? Я не знайшов таких питань у Go repo на github. Це сумно і дивно.
Ігор Петров

1
Як map[string] boolпорівнювати з map[string] struct{}. map[string] struct{}здається, що хак особливо ініціалізує порожню структуруstruct {}{}
vadasambar

@IgorPetrov погодився, я здивований, що така основна особливість вже не в процесі виконання.
jcollum

180

Ні, такого методу не існує, але тривіально писати:

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

Ви можете використовувати карту, якщо цей пошук є важливою частиною вашого коду, але карти також коштують.


257
Насправді це не банально, тому що ви повинні написати одну для кожного використовуваного типу, і оскільки немає перевантаження, ви повинні називати кожну функцію по-різному, як у C. append () може працювати загально, оскільки вона має спеціальну підтримку виконання. Загальна зміст корисна з тієї ж причини, але насправді загальне рішення - це лише підтримка генерики в мові.
Елофф

15
@Eloffinterface{}
Алекс Локвуд

2
@Alex Lockwood чи справді це буде працювати з інтерфейсами?
Ory Band

101
trivial == 7 рядків коду, включаючи 1 цикл, 1 гілка, якщо оператор та 1 порівняння? Я думаю, що мені чогось тут не вистачає ...
tothemario

3
Але чому б не додати їх у сам ядро?
Luna


11

Замість використання A slice, mapможе бути кращим рішенням.

простий приклад:

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
У своєму нинішньому вигляді цей код не приносить користі, оскільки немає сенсу будувати карту з фрагмента, якщо ви збираєтесь використовувати його лише один раз. - Щоб бути корисним, цей код повинен скоріше забезпечувати функцію, sliceToMapяка виконує всю підготовку. Після цього запит на карту є тривіальним та ефективним.
Roland Illig

9

Пакет сортування надає будівельні блоки, якщо ваш фрагмент відсортований або ви готові його сортувати.

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
}

SearchStringобіцяє повернення the index to insert x if x is not present (it could be len(a)), тому перевірка цього виявляє, чи містить рядок відсортований фрагмент.


З точки зору часу, регулярний пошук є, O(n)і це рішення робить це O(n*log(n)).
плесів

@plesiv - це двійковий пошук, AFAICS. Хіба це не зробить це O (log n)?
Генрік Аастед Сьоренсен

так, бінарний пошук і функція containsє O(log(n)), але загальний підхід O(n*log(n))обумовлений родом.
плесів

3

Ви можете використовувати пакет відображення для ітерації інтерфейсу, конкретним типом якого є фрагмент:

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
Звичайно, ви можете використовувати пакет, що відображає, але тільки тому, що можете, не означає, що слід. Рефлексія дуже дорога.
Джастін

3

Якщо карту для пошуку предметів на основі ключа не представляється можливим , ви можете розглянути інструмент годерив . Goderive створює специфічну для конкретного типу реалізацію методу містить, що робить ваш код читабельним та ефективним.

Приклад;

type Foo struct {
    Field1 string
    Field2 int
} 

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

Для створення методу deriveContainsFoo:

  • Встановити goderive за допомогою go get -u github.com/awalterschulze/goderive
  • Запустіть goderive ./...у папці робочої області

Цей метод буде створений для deriveContains:

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

Goderive має підтримку цілого ряду інших корисних допоміжних методів для застосування функціонального стилю програмування.


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

Тут не впевнені, що дженерики потрібні. Вам просто потрібен контракт на бажану поведінку. Виконувати наступне - це не більше ніж те, що вам доведеться робити на інших мовах, якби ви хотіли, щоб ваші власні об’єкти поводили себе у колекціях, наприклад, замінивши рівняння Equals () та 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
}

1
"це не більше ніж те, що вам доведеться робити на інших мовах" насправді не відповідає дійсності - наприклад, в C # Contains()реалізовано далі List<T>, тому вам потрібно коли-небудь реалізувати Equals()для цієї роботи.
Джордж

1

Я створив дуже простий орієнтир з рішеннями з цих відповідей.

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

Це не справжній орієнтир, тому що спочатку я не вставив занадто багато елементів, але не соромтеся розщедритися та змінити його.


Я подумав про це, але він не настільки репрезентативний через те, що моя машина не така потужна.
Ф. Норберт

0

Це може вважатися трохи "хакі", але залежно від розміру та вмісту фрагмента, ви можете з'єднати фрагмент разом та здійснити пошук рядків.

Наприклад, у вас є фрагмент, що містить значення одного слова (наприклад, "так", "ні", "можливо"). Ці результати додаються до фрагмента. Якщо ви хочете перевірити, чи містить цей фрагмент якісь результати "можливо", ви можете використовувати

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

Наскільки це насправді підходить, залежить від розміру шматочка і довжини його членів. Можуть виникнути проблеми з ефективністю чи придатністю для великих фрагментів або довгих значень, але для менших часточок кінцевого розміру та простих значень це дійсний одношаровий рядок для досягнення бажаного результату.


Не вдасться в ситуації, коли елементи мають подібний текст, але не зовсім однаковийexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal

Це досить приємний підхід для досягнення швидкого та забрудненого однолінійного рішення. Вам просто потрібно вимагати однозначного роздільника (може бути кома) і виконувати додаткову роботу, щоб ","+strings.Join(exSlice,",")+","",maybe,"
дужками

-1

Стиль ходу:

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
Це не дає відповіді на запитання, а також не дає додаткової інформації.
Croolman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.