How to Update an Existing Element of Std::Set

How to update an existing element of std::set?

Since val is not involved in comparison, it could be declared mutable

struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};

This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.

Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.

Something like:

bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}

Set operation in c++(update existing value)

Key values of elements in a std::set are const for a good reason. Modifying them may destroy the order which is essential for a std::set.

Hence, the solution is to erase the iterator and insert a new one with key *it - sub. Please, note that std::set::erase() returns a new iterator which has to be used in your case to keep the while loop working properly.

#include<iostream>
#include<set>

template <typename T>
std::ostream& operator<<(std::ostream &out, const std::set<T> &values)
{
const char *sep = "{ ";
for (const T &value : values) { out << sep << value; sep = ", "; }
return out << " }";
}

int main()
{
std::set<int> test{ 11, 12, 13, 14, 15 };
std::cout << "test: " << test << '\n';
const int sub = 10;
std::set<int>::iterator iter = test.begin();
while (iter != test.end()) {
const int value = *iter;
iter = test.erase(iter);
test.insert(value - sub);
}
std::cout << "test: " << test << '\n';
}

Output:

test: { 11, 12, 13, 14, 15 }
test: { 1, 2, 3, 4, 5 }

Live Demo on coliru


Changes on the std::set while iterating over it are not a problem in general but can cause subtle issues.

The most important fact is that all used iterators have to be kept intact or may not be used anymore. (That's why the current iterator of the erase element is assigned with the return value of std::set::erase() which is either an intact iterator or the end of set.)

Of course, elements can be inserted as well behind the current iterator. While this is not a problem concerning the std::set it may break the loop of my above example.

To demonstrate it, I changed the above sample a bit. Please, note that I added an additional counter to grant the termination of loop:

#include<iostream>
#include<set>

template <typename T>
std::ostream& operator<<(std::ostream &out, const std::set<T> &values)
{
const char *sep = "{ ";
for (const T &value : values) { out << sep << value; sep = ", "; }
return out << " }";
}

int main()
{
std::set<int> test{ 11, 12, 13, 14, 15 };
std::cout << "test: " << test << '\n';
const int add = 10;
std::set<int>::iterator iter = test.begin();
int n = 7;
while (iter != test.end()) {
if (n-- > 0) {
const int value = *iter;
iter = test.erase(iter);
test.insert(value + add);
} else ++iter;
}
std::cout << "test: " << test << '\n';
}

Output:

test: { 11, 12, 13, 14, 15 }
test: { 23, 24, 25, 31, 32 }

Live Demo on coliru

C++ std::set update is tedious: I can't change an element in place

set returns const_iterators (the standard says set<T>::iterator is const, and that set<T>::const_iterator and set<T>::iterator may in fact be the same type - see 23.2.4/6 in n3000.pdf) because it is an ordered container. If it returned a regular iterator, you'd be allowed to change the items value out from under the container, potentially altering the ordering.

Your solution is the idiomatic way to alter items in a set.

How can i replace element in set?

You have got to erase() one element and insert() the new one. replace is not defined for std::set. See http://www.cplusplus.com/reference/set/set/

Update element in a set of pointer of elements before deletion

Data which is used as key in a std::set may not be changed. Otherwise, the order of std::set is broken and addressing elements in the std::set cannot work anymore.

Out of curiosity, I tried to do it the wrong way.

Although I know it is undefined behavior, I got this running – a nice demonstration that bad things happen:

#include <iostream>
#include <set>
#include <vector>

typedef int Entry;

struct Less {
bool operator()(const Entry *p1, const Entry *p2) const
{
return *p1 < *p2;
}
};

int main()
{
const int N = 10;
std::vector<int> data;
for (int i = 0; i < N; ++i) data.push_back(i);
std::set<Entry*, Less> set;
for (int &value : data) set.insert(&value);
// do it wrong
data[2] = 12;
set.erase(&data[2]);
set.insert(&data[2]);
// check result
for (const Entry *pValue : set) std::cout << ' ' << *pValue;
std::cout << '\n';
// done
return 0;
}

Output:

 0 1 12 3 4 5 6 7 8 9 12

Live Demo on coliru

How to change a set element?

The elements of the set will be in sorted order. If you are allowed to modify an element, then this sorting order can not be maintained. Hence you can not modify the item. You need to erase the existing element and insert a new one.

How to update std::map after using the find method?

std::map::find returns an iterator to the found element (or to the end() if the element was not found). So long as the map is not const, you can modify the element pointed to by the iterator:

std::map<char, int> m;
m.insert(std::make_pair('c', 0)); // c is for cookie

std::map<char, int>::iterator it = m.find('c');
if (it != m.end())
it->second = 42;

How to update recently entered element in std::vector pair int, int ?

Assuming that you want to update the last std::pair input just after inserting to the std::vector<std::pair<int, int>>.

In c++17 you can make use of second overload of std::vector::emplace_back, which returns a referance to the element inserted:

#include <vector>

std::vector<std::pair<int, int>> vec;
auto &pair = vec.emplace_back(1, 3); // construct in-place and get the reference to the inserted element
pair.second = 5; // change the value directly like this

Update:

In c++11, the same can be achieved by the std::vector::insert member, which returns iterator pointing to the inserted element.

#include <vector>

std::vector<std::pair<int, int>> vec;
// insert the element to vec and get the iterator pointing to the element
const auto iter = vec.insert(vec.cend(), { 1, 3 });
iter->second = 5; // change the value


Related Topics



Leave a reply



Submit