How to Modify Values in a Map Using Range Based for Loop

How can I modify values in a map using range based for loop?

You can turn auto into auto& if you want to mutate/modify the container, for instance:

#include <map>
#include <iostream>

int main()
{
std::map<int, int> foobar({{1,1}, {2,2}, {3,3}});
for(auto& p : foobar) {
++p.second;
std::cout << '{' << p.first << ", " << p.second << "} ";
}
std::cout << std::endl;
}

compiles ands outputs


{1, 2} {2, 3} {3, 4}

live example

Can't modify the value of a reference in Range based loop

You can't modify a reference to x because it is const. It is const because iterating a std::set through loop gives only const values.

See solution with const_cast example code at the end of my answer.

It is known that std::set stores all entries in a sorted tree.

Now imagine if you can modify a variable when iterating a loop, it means that after modification sorted order of std::set might be changed. But std::set should always keep invariant of its tree, hence it can't allow to make any modifications thus gives only const values when iterating.

If you need to really modify set entry then you have to take it from set, delete from set and that add again to set. Even if sorted order is not changed after your modification, still you need to reinsert into set.

But there is a hacky workaround - you can make your method delMinentry as having const modifier. Then all fields that it modifies should be marked as mutable. Mutable fields allow modifications from const methods. And std::set allows to call const methods when iterating.

There is one more workaround - you make delMinterm() as const method, but inside this method do const_cast<Term &>(*this).delMintermNonConst(), in other words from const method you can call non-const method if you do const_cast. Also you can do const cast directly on loop variable if you're sure what you do, in other words if you modify Term in such a way that std::set sorted order is not changed:

for (auto &x : PI) {
const_cast<Term &>(x).delMinterm(i);
}

If delMinterm() results in such modification of a Term after which order of std::set may change then you can't do const_cast in code above. In other words if after delMinterm your operator < may give a different result between terms, then you can't do this const cast and you have to reinsert into set (delete Term and add again).

Also don't forget that after reinserting into set you have to redo set iteration loop again from start, because after change to inner structure you can't keep iterating loop running further, iterators are invalidated.

If set's order changes (hence you can't do const_cast) then you have to re-insert values of set, do this by copying values to vector, modifying them through delMinterm(), copying back to set, like this:

std::vector<Term> scopy(PI.cbegin(), PI.cend());
for (auto & x: scopy)
x.delMinterm(i);
PI = std::set<Term>(scopy.cbegin(), scopy.cend());

How to use range-based for() loop with std::map?

Each element of the container is a map<K, V>::value_type, which is a typedef for std::pair<const K, V>. Consequently, in C++17 or higher, you can write

for (auto& [key, value]: myMap) {
std::cout << key << " has value " << value << std::endl;
}

or as

for (const auto& [key, value]: myMap) {
std::cout << key << " has value " << value << std::endl;
}

if you don't plan on modifying the values.

In C++11 and C++14, you can use enhanced for loops to extract out each pair on its own, then manually extract the keys and values:

for (const auto& kv : myMap) {
std::cout << kv.first << " has value " << kv.second << std::endl;
}

You could also consider marking the kv variable const if you want a read-only view of the values.

range-based for loop for private map values

I'm going to first answer this in c++14.

Here is a minimal mapping iteratoroid:

template<class F, class It>
struct iterator_mapped {
decltype(auto) operator*() const {
return f(*it);
}

iterator_mapped( F f_in, It it_in ):
f(std::move(f_in)),
it(std::move(it_in))
{}

iterator_mapped( iterator_mapped const& ) = default;
iterator_mapped( iterator_mapped && ) = default;
iterator_mapped& operator=( iterator_mapped const& ) = default;
iterator_mapped& operator=( iterator_mapped && ) = default;

iterator_mapped& operator++() {
++it;
return *this;
}
iterator_mapped operator++(int) {
auto copy = *this;
++*this;
return copy;
}
friend bool operator==( iterator_mapped const& lhs, iterator_mapped const& rhs ) {
return lhs.it == rhs.it;
}
friend bool operator!=( iterator_mapped const& lhs, iterator_mapped const& rhs ) {
return !(lhs==rhs);
}
private:
F f;
It it;
};

it is not technically an iterator, but it qualifies for for(:) loops.

template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It b, It e ) {
return {std::move(b), std::move(e)};
}

the above is an absolutely minimal iterator range type that can be for(:) iterated.

template<class F, class R>
auto map_range( F&& f, R& r ) {
using std::begin; using std::end;
auto b = begin(r);
auto e = end(r);
using it = iterator_mapped<std::decay_t<F>, decltype(b)>;
return range( it( f, b ), it( f, e ) );
}

note that R& not R&&; taking an rvalue for r here is dangerous.

auto GetStringIterator() const
{
return map_range( [](auto&& pair)->decltype(auto){
return pair.second;
}, m_Items );
}

and done.

Converting this to c++11 is a pain. You have to toss around std::functions in place of lambdas (or write function objects that do the task instead of a lambda), replace decltype(auto) with auto and trailing return types, give the exact type of auto&& arguments to lambdas, etc. You end up with about 25%-50% more code, most of it obscure type chasing.

This is basically what boost::adaptors::map_values does, but this is hand-rolled so you can understand how it works and don't have a boost dependency.

Updating class member values inside a range based for loop in C++

Your loop makes a copy of the element into m every iteration. Your changes to m will be to that copy. Make m a reference to the element in the container instead:

     for(auto& m : mList)

Iterate values of a map using range-based for loop

From C++20, you can use views::values to get at the values of a std::map, or a vector<pair> for that matter:

for (auto v : m | std::views::values)  // m is some map
// ...

demo

You can similarly get at the keys with views::keys.

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

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;
}

Can I easily iterate over the values of a map using a range-based for loop?

The magic lies with Boost.Range's map_values adaptor:

#include <boost/range/adaptor/map.hpp>

for(auto&& i : foo | boost::adaptors::map_values){
i->bar();
}

And it's officially called a "range-based for loop", not a "foreach loop". :)

Setting vector elements in range-based for loop

Change this loop statement

for(auto n: *CTdata)

to

for(auto &n : *CTdata)

that is you have to use references to elements of the vector.



Related Topics



Leave a reply



Submit