I am looking for a C++ analogs for a map
or filter
from Python programming language. The first of them applies some function to every item of iterable and return a list of the results, the second constructs a list from those elements of iterable for which function returns true.
I would like to use the similar functionality in C++:
Are there any fine implementations of Python's map and filter in C++?
In this short example I am trying to work it out using such tools as boost::bind
and std::for_each
and I face with a difficulties. The std::vector<std::string> result
should contain all the strings std::vector<std::string> raw
that lexicographicaly higher than the last string from stdin. But in fact the result
container is still empty at the return point.
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
void filter_strings(std::string& current, std::string& last, std::vector<std::string>& results)
{
if (current > last)
{
results.push_back(current);
std::cout << "Matched: " << current << std::endl;
}
}
int main()
{
std::vector<std::string> raw, result;
std::string input, last;
//Populate first container with a data
while(std::getline(std::cin, input))
raw.push_back(input);
last = raw.back();
//Put into result vector all strings which lexicographically higher than the last one
std::for_each(raw.begin(), raw.end(), boost::bind(&filter_strings, _1, last, result));
//For some reason the resulting container is empty
std::cout << "Results: " << result.size() << std::endl;
return 0;
}
The input and the output:
[vitaly@thermaltake 1]$ ./9_boost_bind
121
123
122
120 //Ctrl+D key press
Matched: 121
Matched: 123
Matched: 122
Results: 0
Any help will be appreciated.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With