C++
map
for_each
programming
iteration

Use of for_each on map elements

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

std::for_each works perfectly well with std::map, because a map exposes standard iterators just like other STL containers. The only extra detail is that each element is a key-value pair whose key is effectively immutable during iteration.

What a Map Element Looks Like

When you iterate over std::map<Key, Value>, each element has type:

cpp
std::pair<const Key, Value>

That const on the key is important. You can read the key and modify the mapped value, but you cannot assign a new key through the iterator.

Here is a simple read-only example:

cpp
1#include <algorithm>
2#include <iostream>
3#include <map>
4#include <string>
5
6int main() {
7    std::map<std::string, int> scores{
8        {"alice", 10},
9        {"bob", 20},
10        {"cara", 30}
11    };
12
13    std::for_each(scores.begin(), scores.end(), [](const auto& entry) {
14        std::cout << entry.first << ": " << entry.second << '\n';
15    });
16}

std::for_each simply walks the iterators from begin() to end() and applies the lambda to each element.

Modifying the Mapped Value

If you want to change the value part, take the entry by non-const reference:

cpp
1#include <algorithm>
2#include <iostream>
3#include <map>
4#include <string>
5
6int main() {
7    std::map<std::string, int> scores{
8        {"alice", 10},
9        {"bob", 20}
10    };
11
12    std::for_each(scores.begin(), scores.end(), [](auto& entry) {
13        entry.second += 5;
14    });
15
16    for (const auto& entry : scores) {
17        std::cout << entry.first << ": " << entry.second << '\n';
18    }
19}

This is legal because entry.second is mutable.

What is not legal is:

cpp
// entry.first = "new-key";  // does not compile

Map keys determine ordering inside the tree structure, so changing them in place would break the container invariants.

for_each Versus Range-Based for

In modern C++, a range-based loop is often simpler:

cpp
for (const auto& [name, score] : scores) {
    std::cout << name << ": " << score << '\n';
}

For straightforward iteration, this is usually easier to read than std::for_each.

std::for_each still makes sense when:

  • you are already writing in an algorithm-heavy STL style
  • you want to reuse a function object
  • you want the operation packaged as a callable unit

It is valid, just not always the clearest choice.

Using a Named Function Object

If the logic is reusable, a named callable can be cleaner than an inline lambda:

cpp
1#include <algorithm>
2#include <iostream>
3#include <map>
4#include <string>
5
6struct Printer {
7    void operator()(const std::pair<const std::string, int>& entry) const {
8        std::cout << entry.first << ": " << entry.second << '\n';
9    }
10};
11
12int main() {
13    std::map<std::string, int> scores{
14        {"alice", 10},
15        {"bob", 20}
16    };
17
18    std::for_each(scores.begin(), scores.end(), Printer{});
19}

This is one reason for_each exists in the first place: algorithms can operate with ordinary function objects, not just explicit loops.

Ordering and Complexity

Because std::map is ordered, iteration visits elements in key order. std::for_each does not change that. It just consumes the iterators in their natural sequence.

If key ordering is not needed, std::unordered_map may be a better container, but the same for_each pattern still applies conceptually.

Erasing While Iterating Needs Care

One trap is trying to erase elements from the map inside a for_each traversal. Structural modification during active iteration is tricky and should be handled with iterator-aware loops or collected keys for later removal.

For example, safer removal often looks like:

cpp
1for (auto it = scores.begin(); it != scores.end(); ) {
2    if (it->second < 15) {
3        it = scores.erase(it);
4    } else {
5        ++it;
6    }
7}

That is much safer than erasing from inside a for_each lambda.

Common Pitfalls

The most common mistake is forgetting that the element is a pair, not just the mapped value.

Another common mistake is trying to modify entry.first, which is the key and therefore effectively immutable through map iteration.

A third pitfall is using std::for_each for very simple loops where a range-based for is clearer to most readers.

Finally, be careful with structural modifications such as erase or insert during traversal. If the container itself is changing, an ordinary iterator loop is often the better tool.

Summary

  • 'std::for_each works normally with std::map iterators'
  • Each map element is a std::pair<const Key, Value>
  • You may modify the mapped value, but not the key
  • Range-based for loops are often clearer for simple iteration
  • Avoid structural container changes inside a for_each traversal unless you are handling iterators very carefully

Course illustration
Course illustration

All Rights Reserved.