How can I make std::find_if and std::map work together using some boost library?
Asked Answered
E

1

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. :-)

Erepsin answered 7/9, 2011 at 16:34 Comment(6)
Do you want to find only the values (in which case boost.org/doc/libs/release/libs/range/doc/html/range/reference/… is the answer) or the iterator to the whole key,value pair where value satisfied the predicate?Paraffin
@Cubbi: That is not the answer if that doesn't work with std::find_if which returns an iterator of the map.Erepsin
@Nawaz: I don't know if std::find_if is required. If not, I'd go with cplusplus.com/reference/stl/map/upper_bound which should be faster, and only one simple line of code.Gemination
@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.Erepsin
@Nawaz: whoa, my bad. I didn't obvserve that you were finding the greatest value. std::find_if then.Gemination
Of course if you wanted to make such queries frequently, I would advise either a BiMap or a MultiIndex :)Lavernalaverne
R
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"
}
Renin answered 7/9, 2011 at 16:55 Comment(5)
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.Erepsin
Alright. I tried compiling it, and it failed at compilation : ideone.com/EgALCErepsin
@Nawaz : Ideone has Boost 1.39; Boost.Phoenix became a standalone library only in Boost 1.47. Try it locally.Renin
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?Erepsin
@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): ideone.com/8myycRenin

© 2022 - 2024 — McMap. All rights reserved.