How to safely "pop-front" from std::map without extra copy? - c++

I have a std::map object.
std::map<std::string, std::string> m;
m.insert({ "abcd", "foo" });
m.insert({ "1234", "bar" });
and I want to get and remove the first element, like:
auto iter = m.begin();
auto [key, value] = std::move(*iter);
m.erase(iter);
do_something_with(key, value);
Is this considered safe?
(Moving from the iterator should make the key an empty string, which makes the m an invalid map.)

You can use std::map::extract like this:
auto nh = m.extract(m.begin());
and then use the key and value like this:
do_something(nh.key(), nh.mapped());
This has the needed property that no extra copies are made.

Is this considered safe?
On the condition that the map isn't empty, yes.
However, note that the key will be a deep copy; not moved one. This is because the the key of the map element is const.
How to safely “pop-front” from std::map without extra copy?
It is possible to move from the key too, if you use the extract member function:
auto handle = m.extract(m.begin());
// if you need separate objects:
auto key = std::move(handle.key());
auto mapped = std::move(handle.mapped());

Use std map extract. Using the resulting node handle, move the key/value to your key/value variables.
Prior to std map extract, this isn't fully possible. std map extract was added to let you do this, and similar operations like splicing maps.

Related

Find and remove unique_ptr from a map

I have a map<std::string, std::unique_ptr<Cls>> my_map. I would like to move out some value from this map, to have the following:
std::unique_ptr<Cls> cls = my_map.get_and_erase("some str");
erase doesn't return a value unfortunately. What is my best approach here?
Since C++17 you have std::map::extract:
// if the key exists in the map, it'll be deleted after this:
auto cls_node = my_map.extract("some str");
if(not cls_node.empty()) // cls_node.mapped() gives access to the mapped value
You can use the following algorithm:
Use find to get an iterator to the element.
Move from the element.
Erase using the iterator.

sorting std::map by value

Right now I have a map and I need to sort it by value(int), and then by key(string) if there is a tie. I know I would need to write a customized comparison function for this, however so far I haven't been able to make it work.
(I need to store my stuffs in a map since the strings are words and ints are the frequencies and I will need to 'find' the pairs by searching the keys later)
The std::map can only be sorted by key (string in your case).
If you need to sort it by value as well, you'd need to create a std::multimap with the int as key and the string as value, and populate it by iterating over the map.
Alternatively, you could also create a vector<pair<int,string>> that you populate by iteration over the map and just use std::sort().
You can use a std::multiset<std::pair<int, std::string>>
With the information given, it's a bit of a guessing game, but unless you are shuffling massive amounts of data, this may do.
using entry = std::pair<std::string, int>;
using CompareFunc = bool(*)(const entry&, const entry&);
using sortset = std::set<entry, CompareFunc>;
sortset bv(themap.begin(), themap.end(), [](auto& a, auto&b){ a.second!=b.second?a.second<b.second:a.first<b.first; });
for(const auto& d : bv) {
//
}

Insert into container of container if new or append to existing

I have a container that looks like this:
std::map<std::string, std::vector<double>> id_values;
I will iterate through other pairs of doubles and strings, and I want to add a new element to the map if it doesn't exist, or append to the vector if it does. Is there a more succinct solution than the following?
auto loc = id_values.find(key);
if (loc != id_values.end()) {
loc->second.push_back(val);
} else {
loc.insert({key, {val}});
}
I suppose I could do a ternary operator but I feel that will make the code less readable, I'm more wondering if there is a better pattern for what I'm trying to achieve rather than conditional.
You can just use operator[]. It will return a reference to the object with the specified key if it is in the map, or if the lookup fails, it will create a new value-initialized object (an empty vector in your case) and return a reference to the new object.
id_values[key].push_back(val);
Alternatively, if you need to use different constructor arguments instead of default-constructing the mapped_type, you can use try_emplace (or regular emplace if you can't use C++17):
auto [itr, inserted] = id_values.try_emplace(key, vector_constructor_args...);
itr->second.push_back(val);
Hello I think what you want to do is ether to insert an element in your container using the method insert or the operator[].
By using the operator [] what you want to do is simply:
for(auto it: map1)
if(map2.find(it.first) == map2.end() )
map2.insert(map2.end(),it);

Why C++ map.insert() doesn't overwrite

In the code below:
#include <map>
#include <utility>
#include <iostream>
using namespace std;
int main(){
pair<int,int> p1(1,1);
pair<int,int> p2(1,2);
map<int,int> m;
m.insert(p1);
m.insert(p2);
cout << "Map value: "<< m.at(1) << endl;
}
It printed out : Map value: 1, why m.insert(p2) doesn't overwrite the previous entity in the map?
map.insert() only inserts if the container doesn't already contain an element with an equivalent key.
You should use operator[] instead:
m[p2.first] = p2.second;
In the std::map::insert reference it is said that:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
Update as of C++17 There is now the std::map::insert_or_assign() member function:
m.insert_or_assign(p1);
As the name suggests, if the key is already present then the value is assigned (and the key object kept) rather than erasing and freshly copy constructing the key and value. (So it's equivalent to the first of the two pre-C++17 snippets below.)
If you want an iterator pointing at the (new or updated) element, you again need to pick the value out of the returned pair. Since you're using C++17, you can now use a structured binding:
auto [it, wasInserted] = m.insert_or_assign(p1);
Before C++17 Putting together the other answers, if you want to avoid the assumption of being default constructable you get insert-with-overwrite code that looks like this:
auto itAndWasInserted = m.insert(p1);
if (!itAndWasInserted.second) {
*(itAndWasInserted.first) = p1;
}
In the above snippet, if the element is already present then the new value is assigned to it. That's usually what you want. If you instead want to construct rather than assign the new value, but still want to avoid a second seek (after you've erased the original value), you end up with this monster:
auto itAndWasInserted = m.insert(p1);
auto it = itAndWasInserted.first;
if (!itAndWasInserted.second) {
auto afterIt = m.erase(it);
auto newItAndWasInserted = m.insert(afterIt, p1); // Hint form of insert
it = newItAndWasInserted.first;
}
At the end of the code block, it is an iterator pointing at the just-inserted element.
Realistically, in most cases you probably just want to use yizzlez's suggestion of operator[], but I thought it would be good to note the theoretically best answer.
It doesn't overwrite. However if you check the return value, there is a std::pair<iterator, bool>. If bool is true, then it was inserted. If the bool is false, then it was not inserted because of a collision. At that point, you can then overwrite the data yourself by writing to the iterator.
This is supposed to happen. map.insert() will only insert elements into the container if it doesn't already contain any elements, so this will ignore the later value elements assigned to it.

std::map - Element access without exception and without insertion

I have a recurrent pattern with the use of std::map.
I want to retrieve the value only when the key is present, otherwise I don't want to insert element. Currently I'm using count(key) or find(key) (which one is better? from the documentation the complexity seems to be the same) and if them returns a positive value that I access the map. However I would like to avoid the use of two operations on the map. Something like:
map<string, int> myMap;
int returnvalue;
boole result = myMap.get("key1",returnValue)
if(result){
\\ use returnValue
}
Reading the std::map documentation on cplusplus.com I found two functions for accessing map elements:
at(): which throws an excpetion if the key is not present
[]: which insert a new value if the key is not present
None of them satisfy my necessity.
Use map::find:
auto it = myMap.find(key);
if (it != myMap.end())
{
// use it->second
}
else
{
// not found
}
This part was easy. The harder problem is when you want to look up if an element exists and return it if it does, but otherwise insert a new element at that key, all without searching the map twice. For that you need to use lower_bound followed by hinted insertion.
using count() for sure the key is exists
then uses find() to get the k/v pair
if (myMap.count(key))
{
auto it = myMap.find(key)
}
else
{
// not found
}