John Humphreys
John Humphreys

Reputation: 39354

std::map find_if condition style confusion

I'd like to use std::find_if to search for the first element in my map that has a certain value in a specific element of its value structure. I'm a little confused though. I think I need to use bind1st or bind2nd, but I'm not positive that's the right way to go.

Here's some pseudo-code:

struct ValueType { int x, int y, int z };

std::map<int, ValueType> myMap;

... {populate map}

std::map<int, ValueType>::iterator pos = std::find_if(myMap.begin(), myMap.end(), <?>); 

So, let's say that I wanted to find the first element of the map where the .x member of the ValueType was equal to a certain integer value (which can change each call).

What would be the best way to write a function or function object to achieve this? I understand that the <?> has to be a unary predicate which makes me think I'll need bind1st or bind2nd to provide the integer value I'm checking for, but I'm not sure how to go about it. It's been way too long since I looked at this stuff! >.<

Upvotes: 20

Views: 54219

Answers (8)

Unkle George
Unkle George

Reputation: 136

For the lazy, use a C++17 auto lambda, then you don't need to be verbose with the type.

const auto it = std::find_if(myMap.begin(), myMap.end(), [&val](const auto &it) { 
      return it.second.x == val; // Comparing with the object
   }
);

Upvotes: 9

Al Conrad
Al Conrad

Reputation: 1618

Building on all the answers above I cheat by using decltype with C++11 semantics.

auto beg_ = myMap.begin();
auto end_ = myMap.end();
auto it = find_if(beg_, end_,
    [&some_val](decltype(*beg_) & vt) {
        return vt.second == some_val;});
if (end_ != it) {
    auto key_found = (*it).first;
} else {
    // throw error not found.
}

Upvotes: 6

Andriy Tylychko
Andriy Tylychko

Reputation: 16286

using Boost.Bind and Boost.Lambda:

...
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
...
typedef std::map<int, ValueType> MapType;
...
MapType::iterator pos = std::find_if(myMap.begin(), myMap.end(), 
    boost::bind(&ValueType::y, boost::bind(&MapType::iterator::value_type::second, _1)) == magic_number);

Upvotes: 0

Tyler Hyndman
Tyler Hyndman

Reputation: 1400

You can use a lambda function

int val = ...;
auto it = std::find_if(myMap.begin(), myMap.end(), 
   [val](const std::pair<int, ValueType> & t) -> bool { 
      return t.second.x == val;
   }
);

But as Kirill V. Lyadvinsky answer suggests the "first" element may not be what you expect.

Upvotes: 31

Christian Rau
Christian Rau

Reputation: 45968

This doesn't have anything to do with std::bind1st or std::bind2nd. First of all, you have to keep in mind that the elements of a map are key-value pairs, in your case std::pair<int,ValueType>. Then you just need a predicate that compares the x member of the second member of yuch a pair against a specific value:

struct XEquals : std::unary_function<std::pair<int,ValueType>,bool>
{
    XEquals(int _x)
        : x(_x) {}
    bool operator()(const std::pair<int,ValueType> &v) const
        { return p.second.x == x; }
    int x;
};

Upvotes: 2

Kirill V. Lyadvinsky
Kirill V. Lyadvinsky

Reputation: 99715

Elements in the map are not sorted by value, they are sorted according to the key. So the phrase "the first element" has not much sense.

To find some element (not the first) that has x equal to some value you can write the functor as follows:

struct check_x
{
  check_x( int x ) : x_(x) {}
  bool operator()( const std::pair<int, ValueType>& v ) const 
  { 
    return v.second.x == x_; 
  }
private:
  int x_;
};

Then use it as follows:

// find any element where x equal to 10
std::find_if( myMap.begin(), myMap.end(), check_x(10) );

Upvotes: 21

Narek
Narek

Reputation: 39901

If you want to search also in values then may be better to use Boost Bimap in order not to be slow?

Upvotes: 0

John
John

Reputation: 2326

struct Pred
{
    Pred(int x) : x_(x) { }
    bool operator()(const std::pair<int, ValueType>& p)
    {
        return (x_ == p.second.x);
    }
private:
    int x_;
};

... = std::find_if(myMap.begin(), myMap.end(), Pred(NUMBER));

Upvotes: 4

Related Questions