views:

1439

answers:

5

How do you check that an element is in a set?

Is there a simpler equivalent of the following code:

myset.find(x) != myset.end()
+11  A: 

The typical way to check for existence in many STL containers is:

const bool is_in = container.find(element) != container.end();
unwind
@Paul - In other words, no.
Dominic Rodger
this is specific for sets and maps. vectors, lists etc. don't have a find member function.
wilhelmtell
thank you very much.
poulejapon
Yes there is - see the answer that uses count() below.
Arkadiy
+1  A: 

Write your own:

template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
  return container.find(elem) != container.end();
}
stefaanv
just did so :template<class T>static inline bool contains(const std::set<T>}
poulejapon
@paul don't create static global functions. put your function in an anonymous namespace instead: that's the C++ way of creating functions that won't link into other compilation units. also, your T parameter should be a const reference, for const-correctness and for efficiency.
wilhelmtell
-1: Not templated and not *at all* in STL style. This is fine if you aren't using STL, but if you are using STL you should at least attempt to follow its standards.
280Z28
@wilhelmtell but what if I want it inline?
poulejapon
@280Z28 Could you give us your version? I'm not really familiar with STL style...
poulejapon
@Paul: I already did before posting that comment.
280Z28
@280Z28: I'm sorry that my code is not to your standards, I was just showing that if you don't like STL's interface, you can write your own. Jeez, not templated? How templated does it have to be? Your example looks fine, that doesn't mean that mine is bad. It is just more focused on the set as was asked by the OP.
stefaanv
@stefannv: I know I'm picky on things other people aren't. I just hate downvotes without an explanation, so I gave mine. For what it's worth, if you gave that solution in an interview, it'd be a serious step towards the "hire" side. It's practical, succinct, and efficient, I just think the name is not as intuitive as it could be, it could apply to more containers, and the parameters are in the opposite order of several other functions.
280Z28
@280Z28: I was just making a point. I thought that people would be intelligent enough to get the picture.
stefaanv
+3  A: 

Another way of simply telling if an element exists is to check the count()

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

Most of the times, however, I find myself needing access to the element wherever I check for its existence.

So I'd have to find the iterator anyway. Then, of course, it's better to simply compare it to end too.

set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
   // do something with *it
}
Pieter
Note that using `count()` instead of `find()` is never better but potentially worse. This is because `find()` will return after the first match, `count()` will always iterate over all elements.
Frerich Raabe
@Frerich that's only relevant for `multiset` and `multimap` I thought? Still good to point out though :)
Pieter
std::set is typically implemented with an ordered tree structure, so count() and find() will both have O(logn). Neither will iterate over all elements in the set.
Alan
A: 

Just to clarify, the reason why there is no member like contains() in these container types is because it would open you up to writing inefficient code. Such a method would probably just do a this->find(key) != this->end() internally, but consider what you do when the key is indeed present; in most cases you'll then want to get the element and do something with it. This means you'd have to do a second find(), which is inefficient. It's better to use find directly, so you can cache your result, like so:

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

Of course, if you don't care about efficiency, you can always roll your own, but in that case you probably shouldn't be using C++... ;)

Adhemar
+2  A: 

If you were going to add a contains function, it might look like this:

#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();
}

This works with std::set, other STL containers, and even fixed-length arrays:

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

Edit:

As pointed out in the comments, I unintentionally used a function new to C++0x (std::begin and std::end). Here is the near-trivial implementation from 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);
    }

}
280Z28
This works, but is inefficient, as explained below.
Adhemar
Thank you very much!!!
poulejapon
@Adhemar, it actually *was* inefficient, but not at all for the reason you mentioned.
280Z28
@Paul: Make sure that you include the specialization for `std::set`, and remember that it's only appropriate if the *only* thing you need to know is existence.
280Z28
@280Z28: std::begin(container)? What STL standard is that? It doesn't compile on my gcc.
stefaanv
@stefannv: heh, it's new for C++0x. I added the implementation from my compiler above.
280Z28
what about container.begin()?
poulejapon
@Paul: That doesn't work for fixed length arrays. You'll have to either 1) change `std::begin(container)` to `container.begin()` (same for end) or 2) include the new utility functions at the bottom.
280Z28
You may use boost::begin() and boost::end() from Boost range library.
Benoît
@280Z28, it's still inefficient for the reasons I did mention.
Adhemar
@Adhemar: If you know the set contains a value, then you already the value. The only reason you'd need the iterator is to erase the element from the set. If *all* you need is to know whether or not a collection contains a value, then this solution is no less efficient than any other solution.
280Z28
@280Z28: That's a lot of ifs. I'm talking about the general case and explaining why this method is not provided by the STL. In many cases all you have is a key, and you're finding the associated value, or as you say you need the iterator to delete the element.
Adhemar