C++ map, find element and insert it into another map (without copying) - c++

How to correctly find element from source map and insert it into another map?
std::map<int, std::shared_prt<Obj>> src_map
std::map<int, std::shared_prt<Obj>> target_map
int key = 6;
auto found_elem = src_map.find(key);
if (found_elem != src_map.end()) {
if (target_map.find(key) == target_map.end()) {
target_map.insert(found_elem ); <---- How to correctly insert found element from src_map to target_map
}
}

target_map.insert(found_elem);
found_elem is an iterator, you need to insert the value it refers to:
target_map.insert(*found_elem);
Also this could be done more efficiently:
if (target_map.find(key) == target_map.end()) {
target_map.insert(found_elem);
}
You do the lookup twice. Once in find and again in insert.
It's better to just try to insert it, and if you need to know whether it was inserted check the return value:
auto inserted = target_map.insert(*found_elem);
// inserted.first is the iterator to the element with the desired key
// inserted.second is true if a new element was inserted, false if the key already existed
Other options for putting it in the map are to find the position where it belongs, then insert at that position if it's not there already:
auto lower = target_map.lower_bound(key);
if (lower == target_map.end() || lower->first != key) {
target_map.insert(lower, *found_elem);
}
Another option is:
auto& val = target_map[found_elem->first];
if (!val)
val = found_elem->second;
but this is not exactly the same, because if the key already exists in the map with an empty shared_ptr as the value then the value will get replaced. Depending whether you can have empty shared_ptr objects in the map that might not be correct for your program.
Yet another, with slightly different meaning again, is:
target_map[found_elem->first] = found_elem->second;

In current declaration
std::map<int, Obj> src_map
std::map<int, Obj> target_map
You can't have one Obj instance in memory connected to both maps. Either you remove Obj from src_map and put in target_map or change declaration to;
std::map<int, Obj*> src_map
std::map<int, Obj*> target_map
or any other pointer type (shared_ptr as suggested in comment), without this you will always have two independent objects in memory.

Related

Set a default constructor for an element in an unordered_map in case of [] operator

I have this class:
class test_t {
public:
int value;
test_t() { }
test_t(int _value) : value(_value) { }
};
Now I have create a unordered_map with the a int value as key
std::unordered_map<int, test_t> map;
When I will use the operator [] if the key it not exist a new element will be add to the map calling the construct.
test_t & test = map[0];
Now it si possible to tell to the unordered_map to call the other constructor?
i.e. is is possibile to do something like this?
std::unordered_map<int, test_t(5)> map;
in the means that every new element will be create whit the construction with value 5?
I know that i can create a construction like this:
test_t(int _value = 5) { }
however the class test is only a example of something more complex.
[] operator value initialize the mapped value if it didn't find it and you cannot change it. You can change your default initializer though.
test_t() { value = 5;}
If you want to insert the value of your choice in case if the key is not in the map, one way would be using find to get an iterator to the key value pair and if the iterator was end iterator, then insert your key value pair.
Optionally as #PaulMcKenzie suggested, you can just use insert, as "it returns a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place."
m.insert({key, test_t(5)});

remove element from std::list by reference

std::list<Reader> readers;
readers.push_back(Reader());
Reader& r = *(readers.begin());
/* at this point, the exact place in list, where the reader was picked out, is forgotten.
Only 'r' shows which element of the list it is. */
readers.erase(r); //<---how to do this?
Clients get the new instances 'reader' objects from a manager/dispatcher. The manager maintains an internal list of whatever was dispatched and invalidates/frees up a cached data if "everyone interested" picked it up by observing the pool of readers dispatched.
When the client is no longer interested in the data, it should return the reader to the manager for removal from the pool. But I don't want the client to keep an iterator - it's absolutely uninterested in guts of the manager and the pool of the readers; only needs this one own reader it got, not an iterator pointing to it. So, for deletion, it calls the manager's cleanup function, with the reference to that single reader.
Is there a nicer way to erase that reader from the list than to iterate through the whole list in search of that one reader the reference leads to?
you can compare the pointers to check if they are same object
readers.remove_if([r=&r](auto& x){return &x==r;});
Your options if you only have a reference to the object is to use std::list::remove
readers.remove(r);
or std::find in conjunction with std::list::erase
readers.erase(std::find(readers.begin(), readers.end(), r));
The former has to iterate the entire list while the latter will stop when it finds the first element and then removes it. For large list this can make a big difference.
Both of these options only work when the items are unique. If you have non unique elements then you can use std::find_if and provide a functor that compares the address of the items. That way you can guarantee you only delete the object the reference actually refers to instead of compares equal to.
readers.erase(std::find_if(readers.begin(), readers.end(), [&](const auto& e) {return &r == &e;}));
Use std::remove in combination with erase
readers.erase(std::remove(readers.begin(), readers.end(), r), readers.end());
Also, u can't delete element from list by value, without iterating it. If you think about it, it doesn't even make sense, because pointers inside the list have to be updated.
If the list can contain equal values then you can do something like the following
#include <iostream>
#include <list>
int main()
{
struct Reader { std::pair<char, int> p; };
std::list<Reader> readers;
readers.push_back({{ 'A', 1 } });
readers.push_back({ { 'A', 2 } });
Reader &rr = readers.back();
readers.push_back({ { 'A', 3 } });
readers.remove_if([&rr](const Reader &r) { return &r == &rr; });
for (const auto &r : readers)
{
std::cout << r.p.first << ' ' << r.p.second << std::endl;
}
return 0;
}
The program output is
A 1
A 3

how check if std::map key exists for object's property update otherwise insert a new one?

in java, I sometimes do this
Map<String, POJO> objmap = new HashMap<String, POJO>();
POJO obj = null;
if ((obj = objMap.get(key)) == null) {
obj = new POJO();
objMap.put(key, obj);
}
obj.setName("something");
obj.setAddress("yeah");
What is the best practice to do similar thing in c++ with std::map?
to create a obj in map if not exist, then update its properties?
Like this:
void insert_or_update(const K & k, const T & t, std::map<K, T> & m)
{
auto p = m.insert(std::make_pair(k, t));
if (!p.second) p.first->second = t;
}
Or:
m[k] = t;
The latter requires T to be default-constructible and assignable.
In C++17 you can also say:
m.insert_or_assign(k, t);
This has fewer restrictions than the above construction and returns information on whether the insertion took place, as well as the iterator to the element.
You want to use the insert function, it returns an iterator and a boolean regarding whether a new object was inserted:
something like this:
typedef map<int,void*> M;
M m;
auto insertion = m.insert(M::value_type(0,nullptr));
if (insertion.second) {
insertion.first->second = new... (// allocate your item or whatever, this is the iterator to it)
}
You can write objmap[key] = value.
See: http://www.cplusplus.com/reference/map/map/operator[]/
std::map<std::string, POJO> mapStr2Pojo;
mapStr2Pojo["something"].setName("something");
mapStr2Pojo["something"].setAddress("yeah");
std::map<>'s operation[] inserts the object if it doesn't find it.
the insertion operation checks whether each inserted element has a key equivalent to the one of an element already in the container, and if so, the element is not inserted, returning an iterator to this existing element
if ( !myMap.insert( std::make_pair( key, value ) ).second )
{
// Element already present...
}

Obtain iterator upon adding to std::map?

Are there any methods of procuring an iterator, when working with a Standard Library map container, which don't require searching throughout the container?
I have a managing class for a map, and I wish to return the iterator associated to items added to the map. I don't want to rely upon find() if at all possible. If I can avoid searches I figure all the better.
std::map<char, bool>::iterator ClassA::Add(char item)
{
mymap[item] = false;
return mymap.get_iterator_lastitem();
}
Perhaps
return mymap.end() - 1;
If you're not using C++11, then
std::map<char, bool>::iterator ClassA::Add(char item)
{
std::pair<std::map<char, bool>::iterator, bool> result = mymap.insert(std::make_pair(item, false));
if(!result.second) {
// Item already exists, modify that existing item
result.first->second = false;
}
return result.first;
}
If you are using C++11 then it is better to use emplace + auto.
std::map<char, bool>::iterator ClassA::Add(char item)
{
auto result = mymap.emplace(item, false);
if(!result.second) {
// Item already exists, modify that existing item
result.first->second = false;
}
return result.first;
}
Live example
Both insert and emplace return a pair of an iterator and a boolean, of which the iterator points to the inserted or existing element and the boolean indicates whether an insertion (true) took place or if not (false) of which the returned iterator points to the already-existing element with the key.

Change the value in std::map from iterator

My application merges two std::map instances. If there are no duplicates, the merge completes without intervention. However, if a duplicate is detected, then the method asks whether the new value should be ignored or overwritten. (This query may be answered by a rule table, message box to the user, or some other logic ... it is just an instance of a class derived from a pure virtual class with a bool confirm() const method.)
If the insert fails, and they decide to overwrite the existing entry, I already have an iterator pointing to the correct item to update. Can I use this iterator to update the value directly, or do I have to call operator[] and take the hit of another lookup?
// typedef std::map<Foo, Foo, Compare> Dictionary;
// Dictionary this_dictionary, other_dictionary;
for (Dictionary::const_iterator i = other_dictionary.begin();
i != other_dictionary.end();
++i) {
std::pair<Dictionary::iterator,bool> ret = this_dictionary.insert(*i);
if (!ret.second && confirmer.confirm()) {
// ???
}
}
You need to use Dictionary::iterator instead of Dictionary::const_iterator in the return from insert.
for (Dictionary::const_iterator i = other_dictionary.begin();
i != other_dictionary.end();
++i) {
// Use standard iterator here
std::pair<Dictionary::iterator,bool> ret = this_dictionary.insert(*i);
if (!ret.second && confirmer.confirm()) {
ret.first->second = i->first;
}
}
You can, but you need to use iterator instead of const_iterator.