Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can C++11 and C++17 Range-Based For Loop iterate to a specific position instead of full range of the map?

Tags:

c++

c++11

c++17

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;
}
like image 905
Fady Samann Avatar asked Nov 17 '25 09:11

Fady Samann


2 Answers

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:

auto copy = std::map<...>{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;
}
like image 150
Innocent Bystander Avatar answered Nov 18 '25 23:11

Innocent Bystander


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.

like image 33
Human-Compiler Avatar answered Nov 18 '25 22:11

Human-Compiler



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!