Reputation: 2652
I am new to Eric Niebler's ranges-v3 library and I would like to solve the following simple problem:
I have a std::map
containing the following:
std::map<std::string, int> map = {
{"THIS", 1}, {"IS", 2}, {"A", 3}, {"TEST", 4}, {"WITH", 5}, {"MORE", 6}, {"KEYS", 7}
};
I have a std::vector
containing a set of keys as follows:
std::vector<std::string> vec = {
"THIS", "IS", "A", "TEST"
};
How do I use the library (ideally using the pipe composition syntax) to filter out pairs from the map containing the keys in the vector and transcode the resultant view to a std::set<std::pair<std::string, int>>
. Additionally to make the transcode interesting, make sure that the values in the map need to be odd. In this case the range code should:
use these keys:
THIS,IS,A,TEST,
against this map
{A,3},{IS,2},{KEYS,7},{MORE,6},{TEST,4},{THIS,1},{WITH,5},
with an odd map value predicate - produce the following result as a different type of container std::set
{A,3},{THIS,1},
This should be relatively straightforward but I cannot figure out the new syntax. Also any insight about eager vs lazy would be a plus to help me understand.
The following stripped down coliru example shows what I am looking for.
Upvotes: 3
Views: 1505
Reputation: 42776
Compared with traversing the entire map
, it is more efficient to just traverse vec
(generally small in size) and find the value corresponding to the key value from the map
since the search operation of the map is logarithmic.
std::set<std::pair<std::string, int>> set;
std::ranges::copy(
vec | std::views::transform([&map](auto& key) { return map.find(key); })
| std::views::filter([&map](auto it) { return it != map.end() &&
it->second % 2 == 1; })
| std::views::transform([](auto it) { return *it; }),
std::inserter(set, set.begin()));
Upvotes: 2
Reputation: 798
Are you looking for something like this:
std::map<std::string, int> map = {{"THIS", 1}, {"IS", 2}, {"A", 3}, {"TEST", 4},
{"WITH", 5}, {"MORE", 6}, {"KEYS", 7}};
std::vector<std::string> keys = {"THIS", "IS", "A", "TEST"};
auto output = map | ranges::views::filter([&](auto &&pair) {
return std::find(keys.begin(), keys.end(), pair.first) != keys.end() && pair.second % 2 == 1;
});
std::set<std::pair<std::string, int>> set(output.begin(), output.end());
for (auto [key, value] : set) std::cout << key << " " << value << std::endl;
Here I have filtered the map with the vector and created a range adaptor closure object
. Then I have created a set from output.
About the laziness, Look at this code:
std::map<std::string, int> map = {{"THIS", 1}, {"IS", 2}, {"A", 3}, {"TEST", 4},
{"WITH", 5}, {"MORE", 6}, {"KEYS", 7}};
std::vector<std::string> keys = {"THIS", "IS", "A", "TEST"};
auto output = map | ranges::views::filter([&](auto &&pair) {
std::cout << "Seen" << std::endl;
return std::find(keys.begin(), keys.end(), pair.first) != keys.end() && pair.second % 2 == 1;
});
std::cout << "I'm lazy!" << std::endl;
std::set<std::pair<std::string, int>> set(output.begin(), output.end());
for (auto [key, value] : set) std::cout << key << " " << value << std::endl;
I have added some prints. The output is:
I'm lazy!
Seen
Seen
Seen
Seen
Seen
Seen
Seen
A 3
THIS 1
Which shows that output
is not evaluated until output
is used to create the set.
Upvotes: 1