スライスのメソッドが含まれています


回答:


226

Mostafaは、そのようなメソッドを書くのは簡単であることをすでに指摘しており、mkbは、sortパッケージのバイナリ検索を使用するためのヒントを与えてくれました。ただし、そのような多くのチェックが含まれる場合は、代わりにマップを使用することも検討してください。

value, ok := yourmap[key]イディオムを使用して特定のマップキーが存在するかどうかを確認するのは簡単です。値に興味がないのでmap[string]struct{}、たとえばを作成することもできます。struct{}ここで空を使用すると、追加のスペースを必要とせず、Goの内部マップタイプがそのような値に最適化されるという利点があります。したがって、map[string] struct{}Goの世界ではセットによく使用されます。


27
またstruct{}{}、要素を追加するときにマップに渡すことができるように、空の構造体の値を取得するために書き込む必要があることにも注意してください。試してみてください。問題が発生した場合は、遠慮なく質問してください。モスタファのソリューションを使用することもできます(大量のデータがない場合)。
tux21b

5
解決策は簡単です、それは本当です。しかし、そのような基本的な機能をランタイムに追加するにはどうすればよいでしょうか。私はgithubのGoリポジトリでそのような問題を発見していません。それは悲しくて奇妙です。
イゴールペトロフ2017年

1
map[string] boolと比較してどうですかmap[string] struct{}map[string] struct{}特に空の構造体を初期化するハックのようですstruct {}{}
vadasambar

@IgorPetrovは同意しました。そのような基本的な機能がランタイムにまだないことに驚いています。
jcollum

179

いいえ、そのようなメソッドは存在しませんが、書くのは簡単です:

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

そのルックアップがコードの重要な部分である場合はマップを使用できますが、マップにもコストがかかります。


257
実際にはそれは簡単ではありません。使用する型ごとに1つずつ記述する必要があり、オーバーロードがないため、Cのように各関数に異なる名前を付ける必要があります。append()は、特別なランタイムサポートがあるため、一般的に機能します。ジェネリックの包含は同じ理由で役立ちますが、実際には、ジェネリックのソリューションは単に言語でのジェネリックのサポートです。
Eloff、2014

15
@Eloffinterface{}
Alex Lockwood

2
@Alex Lockwoodこれは実際にインターフェイスで動作しますか?
Ory Band

101
ささいな== 1つのループ1つの分岐ifステートメントと1つの比較を含む7行のコード?私はここで何か不足していると思います...
tothemario 2016年

3
しかし、なぜこれらをgoコア自体に追加しないのですか?
Luna Lovegood


11

代わりに使用するのではslicemapよりよい解決策かもしれません。

簡単な例:

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、2015年

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はreturnを約束する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)になりませんか?
Henrik AastedSørensen

はい、バイナリ検索と関数containsO(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
もちろん、reflectパッケージを使用できますが、使用できるからといって、そうする必要があるわけではありません。反射は非常に高価です。
ジャスティンオーム

3

キーに基づいてアイテムを見つけるためにマップを使用することが現実的でない場合は、goderiveツールを検討できます。Goderiveは、containsメソッドのタイプ固有の実装を生成し、コードを読みやすく効率的にします。

例;

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は、他のいくつかの便利なヘルパーメソッドをサポートしており、関数型プログラミングスタイルをgoに適用できます。


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

最初はあまり多くの要素を挿入していませんが、フォークして自由に変更できるため、これは実際のベンチマークではありません。


私はそれについて考えましたが、私のマシンはそれほど強力ではないという事実のため、それはそれほど代表的ではありません。
F.ノーベルト

0

少し「ハッキー」と考えられるかもしれませんが、スライスのサイズと内容によっては、スライスを結合して文字列検索を実行できます。

たとえば、単一の単語値を含むスライスがあります(たとえば、「yes」、「no」、「maybe」)。これらの結果はスライスに追加されます。このスライスに「たぶん」結果が含まれているかどうかを確認したい場合は、

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

これがどれほど適切であるかは、スライスのサイズとそのメンバーの長さに依存します。大きなスライスや長い値にはパフォーマンスや適合性の問題があるかもしれませんが、有限サイズで単純な値の小さなスライスの場合、目的の結果を得るには有効な1行です。


要素のテキストが類似しているがまったく同じではない状況では機能しません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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.