C++ tools with the same functionality as Python's filter and map
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Beneath the City Looping
--
Chapters
00:00 C++ Tools With The Same Functionality As Python'S Filter And Map
01:39 Accepted Answer Score 4
02:17 Answer 2 Score 1
03:02 Answer 3 Score 1
04:15 Thank you
--
Full question
https://stackoverflow.com/questions/2186...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #c++ #boost #functionalprogramming
#avk47
ACCEPTED ANSWER
Score 4
As @juanchopanza has suggested, the template functions in the <algorithm> STL header are your best bet.
#include <iostream>
#include <vector>
std::vector<std::string> filter(std::vector<std::string> & raw) {
    std::vector<std::string> result(raw.size());
    std::string last = raw[raw.size() - 1];
    auto it = std::copy_if(raw.begin(), raw.end(), result.begin(),
        [&](std::string s) { return s.compare(last) > 0; });
    result.resize(std::distance(result.begin(), it));
    return result;
}
int main(int argc, const char *argv[])
{
    std::vector<std::string> raw, result;
    std::string input;
    while (std::getline(std::cin, input)) {
        raw.push_back(input);
    }
    result = filter(raw);
    for (size_t i = 0; i < result.size(); i++) {
        std::cout << "Matched: " << result[i] << std::endl;
    }
    std::cout << "Results: " << result.size() << std::endl;
    return 0;
}
Compile and run:
$ clang++ -std=c++11 -o cppfilter main.cpp && ./cppfilter
121
123
122
120  // Ctrl + D pressed
Matched: 121
Matched: 123
Matched: 122
Results: 3
ANSWER 2
Score 1
The reason that your code is not working as you thought it would is that bind() makes a copy of all the parameters. That means that you were adding items to a copy of your std::vector<string> result;
To resolve this issue you need to put your vector into a reference wrapper. That then gets copied but it contains a reference to your result vector. The change is small:
std::for_each(raw.begin(), raw.end(), std::bind(&filter_strings, std::placeholders::_1, last, std::ref(result)));
Note that I'm using the C++11 bind, not boost bind here.
Now if you want to use a lambda to keep the predicate code local to the filter you could:
std::for_each(raw.begin(), raw.end(), [&](std::string& s){ if (s > last) result.push_back(s); });
Or using std::copy_if:
std::copy_if(raw.begin(), raw.end(), std::back_inserter(result), [&](std::string& s){ return s > last; });
ANSWER 3
Score 1
To make your current code work, you have to wrap the result argument to boost::bind inside boost::ref(), otherwise bind will make a copy of your result.
Otherwise, the commenters @juanchopanza and @alexbuisson already gave good answers on this.
Using the plain C++11 standard library (i.e. without Boost), you could implement your above program by replacing the std::for_each() with the following (note that the filter_strings function is not needed anymore and you neeed to #include <iterator> for std::back_inserter):
std::copy_if(raw.begin(), raw.end(), std::back_inserter(result),
    [&](std::string const& current) -> bool {
        if (current > last)
        {
            std::cout << "Matched: " << current << std::endl;
            return true;
        }
        return false;
    }
);
Although this is (probably, if you know the STL) better than your initial approach with custom push_back in for_each it still does not look very nice. Generally, more readable code can be written using Boost.Range, where you can find nearly 1:1 replacements for map and filter: filtered and transformed. For the program above, these would not be particularly helpful, but especially for chained map/filter, using Boost.Range tends to help.