Ответы:
Мостафа уже указал, что такой метод написать тривиально, и mkb подсказал вам использовать двоичный поиск из пакета sort. Но если вы собираетесь делать много таких проверок, вы можете использовать карту.
С помощью этой value, ok := yourmap[key]
идиомы легко проверить, существует ли определенный ключ карты . Поскольку вас не интересует значение, вы можете также создать, map[string]struct{}
например. Использование struct{}
здесь пустого имеет то преимущество, что оно не требует дополнительного пространства, а внутренний тип карты Go оптимизирован для таких значений. Таким образом, map[string] struct{}
это популярный выбор для наборов в мире Go.
struct{}{}
чтобы получить значение пустой структуры, чтобы вы могли передать ее на карту, когда вы хотите добавить элемент. Просто попробуйте, и если у вас возникнут какие-либо проблемы, не стесняйтесь спрашивать. Вы также можете использовать решение Mostafa, если вам легче его понять (если у вас нет огромных объемов данных).
map[string] bool
сравнить с map[string] struct{}
. map[string] struct{}
кажется хаком, особенно инициализация пустой структурыstruct {}{}
Нет, такого метода не существует, но написать тривиально:
func contains(s []int, e int) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
Вы можете использовать карту, если этот поиск является важной частью вашего кода, но карты тоже стоят.
interface{}
Если срез отсортирован, есть бинарный поиск осуществляется в в sort
пакете .
Вместо того чтобы использовать 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))
}
sliceToMap
которая делает всю подготовку. После этого запросы к карте тривиальны и эффективны.
Пакет сортировки предоставляет строительные блоки, если ваш фрагмент отсортирован или вы хотите его отсортировать.
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))
.
contains
есть O(log(n))
, но общий подход O(n*log(n))
обусловлен сортировкой.
Вы можете использовать отражающий пакет для перебора интерфейса, конкретным типом которого является срез:
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
}
Если использовать карту для поиска элементов по ключу нецелесообразно , вы можете воспользоваться инструментом goderive . Goderive генерирует специфическую для типа реализацию метода contains, делая ваш код читабельным и эффективным.
Пример;
type Foo struct {
Field1 string
Field2 int
}
func Test(m Foo) bool {
var allItems []Foo
return deriveContainsFoo(allItems, m)
}
Чтобы сгенерировать метод diverveContainsFoo:
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 поддерживает довольно много других полезных вспомогательных методов для применения функционального стиля программирования в go.
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
}
Не уверен, что здесь нужны дженерики. Вам просто нужен контракт для вашего желаемого поведения. Выполнение следующего не более чем то, что вы должны были бы сделать на других языках, если бы вы хотели, чтобы ваши собственные объекты вели себя в коллекциях, переопределяя, например, 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
}
Contains()
реализован List<T>
, так что вам нужно всего лишь реализовать его Equals()
для этой работы.
Я создал очень простой тест с решениями из этих ответов.
https://gist.github.com/NorbertFenk/7bed6760198800207e84f141c41d93c7
Это не настоящий эталон, потому что изначально я не вставил слишком много элементов, но не стесняюсь раскошелиться и изменить его.
Стиль 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"
})
Это может считаться немного «хакерским», но в зависимости от размера и содержимого фрагмента вы можете объединить фрагмент и выполнить поиск по строке.
Например, у вас есть фрагмент, содержащий значения одного слова (например, «да», «нет», «возможно»). Эти результаты добавляются к срезу. Если вы хотите проверить, содержит ли этот срез какие-либо «возможно» результаты, вы можете использовать
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"]
","+strings.Join(exSlice,",")+","
и",maybe,"