Are there versions of C 11 and C 17 Range-Based For Loop iterators that can iterate to a certain position in map? For example, if a map has 10 key-value elements, then how can I iterate through only the first three key-value elements? The following codes only iterate through the full range of the map.
//C 11
for(auto m: mapData){
cout << m.first << ": " << m.second << endl;
}
//C 17
for(auto [key, val]: mapData){
cout << key << ": " << val << endl;
}
CodePudding user response:
You either need an external counter to make early exit, eg:
int n = 0;
for(auto [k, v] : map)
{
if( n > 10) break;
std::cout << k << ": " << v << std::endl;
}
Or, if you are not afraid of copying the map, you can do:
std::map<...> copy { map.begin(), std::next(map.begin(), 10) };
for(auto [k, v] : copy) std::cout << k << ": " << v << std::endl;
Finally, if you can use C 20, then you can simply do this:
#include <ranges>
for(auto [k, v] : map | std::views::take(10))
{
std::cout << k << ": " << v << std::endl;
}
CodePudding user response:
Range-based for loops are just syntactic-sugar over normal begin()
and end()
calls. Your request of a partial iteration are complicated by the fact that std::map
does not have random-access iterators -- which incurs a cost per iterator incrementing.
To avoid redundant costs, your best option would be to us range-based for loops to a specific counter with a break
:
auto count = 0;
for (const auto& [k,v] : map) {
if ( count > n) { break; }
// process 'k', 'v'
}
If you are looking for a "standard" approach, you can use std::for_each_n
which lets you choose the length. You just need to ensure that the count doesn't exceed the length of container:
auto length = std::min(map.size(), n);
std::for_each_n(map.begin(), n, [&](const auto& kv) {
// process kv
});
Though the std::for_each_n
approach is largely equivalent to the first approach with a counter.
If you expand to support c 20, your options open up a bit since you can construct a std::ranges::subrange
:
for (const auto& [k,v] : std::ranges::subrange(map.begin(), std::advance(map.end(), n)) {
// process 'k', 'v'
}
The downside with this approach is that std::map
iterators are not random-access -- which means you are paying the cost of iterating the sequence twice. For the most part, this isn't really worth it just to try to leverage range-based for
loops.
Edit: See @InnocentBystander's answer with std::views::take(...)
as an alternative, which will effectively produce something equivalent to the count
break
-based approach.