6

This question is inspired from another topic which poses this question:

Find the first value greater than user specified value from a map container

which can be solved in several ways. A typical C++03 solution defines a dedicated function (or functor) and pass it to std::find_if as third argument.

In C++11, one can avoid defining a dedicated function (or functor), and can instead make use of lambda as:

auto it = std:: find_if(m.begin(), mp.end(), 
                    [n](const std::pair<std::string, int> & x) -> bool
                       { return x.second > n; }
                   );

which is the accepted answer.

I'm still looking for a short and cool solution. If it were a vector, then I just learnt a cool solution which makes use of Boost.Phoenix and the solution becomes very concise (ideone demo):

std::vector<int> v = ...;
auto it = std::find_if(v.begin(), v.end(), arg1 > 4);

Here arg1 is a functor object defined in boost::phoenix::arg_names namespace, and the expression arg1>4 evaluates to another functor which then gets passed to std::find_if.

A quick test is (ideone),

std::cout<< (arg1 > 9)(v) << std::endl; //prints 0 if as v > 9 is false, else 1

//or store the functor first and then use it
const auto & f = arg1 > 9;
std::cout<<  f(v) << std::endl; //prints 0 if as v > 9 is false, else 1

My question is, I want to solve the map problem, in a similar way. Is there any such solution? Something like:

auto it = std::find_if(m.begin(),mp.end(), (???).second > n); //m is std::map

Or,

auto it = std::find_if(m.begin(),mp.end(), at<1>(arg1) > n);  //m is std::map

For it to work, the expression at<1>(arg1) > 2 has to evaluate to a functor which takes const std::pair & as argument. My gut feelings tells me that boost has this solution. :-)

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Do you want to find only the values (in which case http://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/adaptors/reference/map_values.html is the answer) or the iterator to the whole key,value pair where value satisfied the predicate? – Cubbi Sep 07 '11 at 16:54
  • @Cubbi: That is not the answer if that doesn't work with `std::find_if` which returns an iterator of the map. – Nawaz Sep 07 '11 at 16:56
  • @Nawaz: I don't know if std::find_if is required. If not, I'd go with http://www.cplusplus.com/reference/stl/map/upper_bound/ which should be faster, and only one simple line of code. – Mooing Duck Sep 07 '11 at 17:26
  • @Mooing: Please read the doc which says `Returns an iterator pointing to the first element in the container whose *key* compares greater than x (using the container's comparison object).` and here I want ot compare *values*, not *keys*. – Nawaz Sep 07 '11 at 17:28
  • @Nawaz: whoa, my bad. I didn't obvserve that you were finding the greatest value. std::find_if then. – Mooing Duck Sep 07 '11 at 17:33
  • 1
    Of course if you wanted to make such queries frequently, I would advise either a BiMap or a MultiIndex :) – Matthieu M. Sep 07 '11 at 18:00

1 Answers1

9

Indeed, Boost.Fusion and Boost.Phoenix have exactly what you want built-in.

If one includes the necessary header to adapt std::pair<> as a conforming Fusion sequence, then one can use Phoenix's lazy version of boost::fusion::at_c<> to access std::pair<>::first or std::pair<>::second (be sure to #include <boost/phoenix/fusion.hpp>).

namespace phx = boost::phoenix;
using phx::arg_names::arg1;

auto it = std::find_if(m.begin(), m.end(), phx::at_c<1>(arg1) > n);

EDIT: Full sample, tested with VC++ 2010 SP1 + Boost 1.47.0:

#include <algorithm>
#include <map>
#include <string>
#include <iostream>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/fusion.hpp>

int main()
{
    namespace phx = boost::phoenix;
    using phx::arg_names::arg1;

    std::map<std::string, int> m;
    m["foo"]    = 1;
    m["bar"]    = 2;
    m["baz"]    = 3;
    m["qux"]    = 4;
    m["quux"]   = 5;
    m["corge"]  = 6;
    m["grault"] = 7;
    m["garply"] = 8;
    m["waldo"]  = 9;
    m["fred"]   = 10;
    m["plugh"]  = 11;
    m["xyzzy"]  = 12;
    m["thud"]   = 13;

    int const n = 6;
    auto it = std::find_if(m.cbegin(), m.cend(), phx::at_c<1>(arg1) > n);
    if (it != m.cend())
        std::cout << it->first << '\n'; // prints "fred"
}
ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • Please post a full working and tested solution. I don't wanna say that it doens't compile or doens't work. Though, I'm trying to do what you suggested. – Nawaz Sep 07 '11 at 17:00
  • Alright. I tried compiling it, and it failed at compilation : http://ideone.com/EgALC – Nawaz Sep 07 '11 at 17:07
  • @Nawaz : Ideone has Boost 1.39; Boost.Phoenix became a standalone library only in Boost 1.47. Try it locally. – ildjarn Sep 07 '11 at 17:14
  • I'm trying to make it work at ideone which is using I think older version of ideone. But then I think that is not the problem. My doubt is that if `at_c<1>(arg1) > n` doesn't evaluated to a functor object which takes `std::pair` as argument, then how can it work? – Nawaz Sep 07 '11 at 17:16
  • 2
    @Nawaz : `phoenix::at_c<1>(arg1) > n` **does** evaluate to a functor taking a `std::pair<>` argument. Here is your sample with corrected includes (for old versions of Boost): http://ideone.com/8myyc – ildjarn Sep 07 '11 at 17:21