要素がstd :: setにあることを確認する方法は?


329

要素がセットに含まれていることをどのように確認しますか?

次のコードのより簡単な同等物はありますか?

myset.find(x) != myset.end()

4
それよりも簡単になる唯一の方法は、ブール述語です:テンプレート<typename T>ブールメンバー(T const&item)。そしてそれはあなたが尋ねているラインの観点から(カバーの下で)実装されます。
ドンウェイクフィールド、

回答:


399

典型的な方法は、次のような多くのSTLコンテナに存在するかどうかを確認するためにstd::mapstd::set、...次のとおりです。

const bool is_in = container.find(element) != container.end();

25
これはセットとマップに固有です。ベクトル、リストなどには、メンバー検索関数はありません。
ヴィルヘルムテル2009年

8
count()を使用したIMOは、Pieterの回答にあるように、単に短く、ブール値に変換されるため、より優れています。私は...なぜこの答えは受け入れてしまったので、多くのポイントを理解していない
ОгњенШобајић

4
完全を期すために、ベクトル/リストはstd :: find:を使用できますstd::find(container.begin(), container.end(), element) != container.end()。もちろんO(N)の問題は残っています...
アコンカグア

10
@MichaelMathewsバリアントの場合:if(container.find(foo) == container.end())最初にツリールックアップを実行して要素を検索する必要があります。見つからない場合は、2番目のツリールックアップを実行して正しい挿入位置を検索する必要があります。元のバリアントにif(container.insert(foo).second) {...}は、単一のツリー検索が1つだけ必要であるという魅力があります...
Aconcagua

23
set.contains(x)C ++ 20標準にはboolを返すa があります。でそれを取得するために2020年まで、私たちを取っている理由私は知らない。
gremwell

215

要素が存在するかどうかを簡単に確認する別の方法は、 count()

if (myset.count(x)) {
   // x is in the set, count is 1
} else {
   // count zero, i.e. x not in the set
}

ただし、ほとんどの場合、その存在を確認するときはいつでも、要素にアクセスする必要があります。

とにかく私はイテレータを見つける必要があります。もちろん、単純に比較することendもお勧めします。

set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
   // do something with *it
}

C ++ 20

C ++ 20ではセットがcontains関数を取得するため、次のURLで説明されているように、次のことが可能になります:https : //stackoverflow.com/a/54197839/895245

if (myset.contains(x)) {
  // x is in the set
} else {
  // no x 
}

102
count()代わりにを使用するfind()ことは決して良いことではありませんが、場合によってはさらに悪いことに注意してください。これは、find()が最初の一致の後に戻り、count()常にすべての要素を反復するためです。
Frerich Raabe

34
@Frerichは関連しているだけだmultisetmultimap私は思いましたか?それでも指摘するのは良いことです:)
ピーター

83
std :: setは通常、順序付けられたツリー構造で実装されるため、count()とfind()はどちらもO(logn)を持ちます。どちらもセット内のすべての要素を反復しません。
アラン、

14
@FrerichRaabe-確かですか?set一致するメンバーを1つだけ含めることが可能なため、この場合、Pieterが指摘するように、最初の要素を見つけた後に停止するような方法で関数を実装しませんか?とにかく役立つ回答!
Dan Nissenbaum、2014年

14
@DanNissenbaumはい、その通りです(+ Peterと+ Alanもそうです)。std:: setの場合、2つの関数のパフォーマンスは同等です。したがって、私のコメントの最初の部分(count()より速くなることはありませんfind())がまだ保持されていても、2番目の部分は実際にはに適用されませんstd::set。しかし、私は別の議論を支持することができると思いますfind():それはより表現力豊かです。つまり、出現数を数える代わりに要素を見つけようとしていることを強調します。
Frerich Raabe 14年

42

明確にするためにcontains()、これらのコンテナータイプのようなメンバーが存在しない理由は、非効率的なコードを記述しやすくなるためです。このような方法はおそらくthis->find(key) != this->end()内部的に行うだけですが、キーが実際に存在する場合はどうするかを検討してください。ほとんどの場合、要素を取得してそれを使って何かをしたいと思うでしょう。これは、2番目のを実行する必要があることを意味しますがfind()、これは非効率的です。findを直接使用する方がよいので、次のように結果をキャッシュできます。

auto it = myContainer.find(key);
if (it != myContainer.end())
{
    // Do something with it, no more lookup needed.
}
else
{
    // Key was not present.
}

もちろん、効率を気にしないのであれば、いつでも自分でロールできますが、その場合はおそらくC ++を使用すべきではありません...;)


44
セットはどうですか?通常は、すでに要素を持っていますが、ちょうどそれがでますかどうかを確認したい。
Elazar Leibovich

8
これがそのようなメソッド/関数がstlに含まれていない実際の理由であるのか、それとも単なる推測に基づくものなのかについて、何か言及はありますか?
Fabio A.

3
@FabioA。それは私の教育を受けた推測です。
Tim

1
@Adhemar、一貫性はSTLの強力な側面ではありません...(list::removeremove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich 2014年

3
自分が何をしているのか知らない人が誤って使用する可能性があるため、機能を含めないことは私には意味がありません。プログラミングは、自分で考え、コードとそのパフォーマンスに責任を持つ人々のためのものです
slawekwin

13

ではC ++ 20我々は最終的に取得しますstd::set::contains方法を。

#include <iostream>
#include <string>
#include <set>

int main()
{
    std::set<std::string> example = {"Do", "not", "panic", "!!!"};

    if(example.contains("panic")) {
        std::cout << "Found\n";
    } else {
        std::cout << "Not found\n";
    }
}

6

contains関数を追加する場合、次のようになります。

#include <algorithm>
#include <iterator>

template<class TInputIterator, class T> inline
bool contains(TInputIterator first, TInputIterator last, const T& value)
{
    return std::find(first, last, value) != last;
}

template<class TContainer, class T> inline
bool contains(const TContainer& container, const T& value)
{
    // This works with more containers but requires std::begin and std::end
    // from C++0x, which you can get either:
    //  1. By using a C++0x compiler or
    //  2. Including the utility functions below.
    return contains(std::begin(container), std::end(container), value);

    // This works pre-C++0x (and without the utility functions below, but doesn't
    // work for fixed-length arrays.
    //return contains(container.begin(), container.end(), value);
}

template<class T> inline
bool contains(const std::set<T>& container, const T& value)
{
    return container.find(value) != container.end();
}

これはstd::set、、他のSTLコンテナ、さらには固定長配列でも機能します。

void test()
{
    std::set<int> set;
    set.insert(1);
    set.insert(4);
    assert(!contains(set, 3));

    int set2[] = { 1, 2, 3 };
    assert(contains(set2, 3));
}

編集:

コメントで指摘したように、C ++ 0x(std::beginおよびstd::end)の新しい関数を誤って使用しました。VS2010からの簡単な実装を次に示します。

namespace std {

template<class _Container> inline
    typename _Container::iterator begin(_Container& _Cont)
    { // get beginning of sequence
    return (_Cont.begin());
    }

template<class _Container> inline
    typename _Container::const_iterator begin(const _Container& _Cont)
    { // get beginning of sequence
    return (_Cont.begin());
    }

template<class _Container> inline
    typename _Container::iterator end(_Container& _Cont)
    { // get end of sequence
    return (_Cont.end());
    }

template<class _Container> inline
    typename _Container::const_iterator end(const _Container& _Cont)
    { // get end of sequence
    return (_Cont.end());
    }

template<class _Ty,
    size_t _Size> inline
    _Ty *begin(_Ty (&_Array)[_Size])
    { // get beginning of array
    return (&_Array[0]);
    }

template<class _Ty,
    size_t _Size> inline
    _Ty *end(_Ty (&_Array)[_Size])
    { // get end of array
    return (&_Array[0] + _Size);
    }

}

1
@Adhemar、それは実際に非効率的でしたが、あなたが言及した理由のためにまったくではありませんでした。
サム・ハーウェル

@Paul:の特殊化が含まれていることを確認してくださいstd::set。また、それが適切なのは、存在を知る必要がある場合のみです。
Sam Harwell

@ 280Z28:std :: begin(container)?それはどのSTL標準ですか?私のgccではコンパイルできません。
stefaanv 2009年

@stefannv:へえ、C ++ 0xの新機能です。上記のコンパイラから実装を追加しました。
サムハーウェル

2
@Adhemar:セットに値が含まれていることがわかっている場合、その値はすでに使用されています。イテレータが必要になる唯一の理由は、セットから要素を消去することです。場合は、すべての必要なコレクションに値が含まれているか否かを知ることであり、この解決策にはあまり効率的で、他のソリューションよりもありません。
サムハーウェル、

4

要素を挿入するときに、要素がセットにあるかどうかを確認することもできます。単一要素のバージョンはペアを返します。そのメンバーのpair :: firstは、新しく挿入された要素またはセット内のすでに対応する要素を指すイテレータに設定されています。ペアのpair :: second要素は、新しい要素が挿入された場合はtrueに設定され、同等の要素がすでに存在する場合はfalseに設定されます。

例:セットにすでに要素として20があるとします。

 std::set<int> myset;
 std::set<int>::iterator it;
 std::pair<std::set<int>::iterator,bool> ret;

 ret=myset.insert(20);
 if(ret.second==false)
 {
     //do nothing

 }
 else
 {
    //do something
 }

 it=ret.first //points to element 20 already in set.

要素がpair :: firstより新しく挿入された場合、セット内の新しい要素の位置を指します。


2

自分で書く:

template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
  return container.find(elem) != container.end();
}

4
たった今:template <class T> static inline bool contains(const std :: set <T>&S、T x){return(S.find(x)!= S.end()); }
fulmicoton 2009年

4
@paulは静的グローバル関数を作成しません。代わりに、匿名の名前空間に関数を配置します。これは、他のコンパイルユニットにリンクしない関数を作成するC ++の方法です。また、constの正確性と効率のために、Tパラメーターはconst参照にする必要があります。
ヴィルヘルムテル2009年

-1:テンプレート化されておらず、STLスタイルでもまったくありません。STLを使用していない場合はこれで問題ありませんが、STLを使用している場合は、少なくともその標準に従うようにしてください。
サムハーウェル、

1
@ 280Z28:私のコードがあなたの標準に合わないのは残念ですが、あなたがSTLのインターフェースが好きでないなら、あなたがあなた自身のものを書くことができることを示していました。テンプレートではなく、Jeez?どのようにテンプレート化する必要がありますか?あなたの例はうまく見えます、それは私のものが悪いということではありません。OPから求められたように、セットに焦点を合わせているだけです。
stefaanv 2009年

1
@ 280Z28:私はただ主張していた。人々はその写真を撮るのに十分な知性があると思いました。
stefaanv 2009年

2

私が使う

if(!my_set.count(that_element)) //Element is present...
;

しかし、それはほど効率的ではありません

if(my_set.find(that_element)!=my_set.end()) ....;

私のバージョンは、コードを書く時間を節約するだけです。競争力のあるコーディングには、この方法を好みます。


はい、count()。ブール式で使用される整数を返す関数が非ゼロをテストしていることを理解できない人は、C / C ++イディオムの大海原に他の多くの問題を抱えることになります。そして、上で述べたように、実際にはセットにとっても効率的である必要があり、それが問題でした。
ロン・バーク

0

andの一般的なcontains関数を書くことができました。std::liststd::vector

template<typename T>
bool contains( const list<T>& container, const T& elt )
{
  return find( container.begin(), container.end(), elt ) != container.end() ;
}

template<typename T>
bool contains( const vector<T>& container, const T& elt )
{
  return find( container.begin(), container.end(), elt ) != container.end() ;
}

// use:
if( contains( yourList, itemInList ) ) // then do something

これにより、構文が少し整理されます。

しかし、私はこれを任意のstlコンテナーで機能させるためにテンプレートテンプレートパラメーターマジックを使用できませんでした。

// NOT WORKING:
template<template<class> class STLContainer, class T>
bool contains( STLContainer<T> container, T elt )
{
  return find( container.begin(), container.end(), elt ) != container.end() ;
}

最後の答えを改善することについてのコメントがあればいいでしょう。


コメントでブロックコードを実際に記述することはできませんが、どう template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
ですか

std :: vectorはテンプレート引数として追加のアロケータを必要とし、std :: setはアロケータと少ないテンプレート引数を必要とするため、機能しません。これらの行は正常に機能します:template <template <class、class> class STLContainer、class T、class A> bool contains(STLContainer <T、A> container、T elt){return find(container.begin()、container.end( )、elt)!= container.end(); } template <template <class、class、class> class STLContainer、class T、class L、class A> bool contains(STLContainer <T、A、L> container、T elt){return find(container.begin()、container .end()、elt)!= container.end(); }
tgmath 2013

0

//一般的な構文

       set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");

/ *以下のコードで、要素4を見つけようとしていますが、それが存在するかどうかに関係なく、整数セットです* /

set<int>::iterator ii = find(set1.begin(),set1.end(),4);
 if(ii!=set1.end())
 {
    cout<<"element found";
    set1.erase(ii);// in case you want to erase that element from set.
 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.