How to effectively reuse mapped value - c++

The program has the following input data:
std::map<std::string, std::vector<int> >
Now I need to convert this data structure into the following:
std::map<int, std::vector<int> >
Fo example:
"key1" => 1
"key2" => 20
etc.
Only the key type is changed, the mapped value is unchanged.
The question is that how I can reuse the mapped key std::vector<int> effectively so that the mappped value is not copied since there is no need to do so.
Here are two ideas that come to my mind:
////////////////////////////////////////////////////
Solution 1>
redefine the interface
from
std::map<int, std::vector<int> >
to
std::map<int, std::vector<int>* >
////////////////////////////////////////////////////
Solution 2>
redefine the interface
from:
std::map<std::string, std::vector<int> >
std::map<int, std::vector<int> >
to:
std::map<std::string, boost::shared_ptr<std::vector<int>> >
std::map<int, boost::shared_ptr<std::vector<int>> >
In both cases, the cost of the copy is simply a copy of a pointer.
Any comment is appreciated

C++11 provides move-operations to move one object to another without copying. VS 2010 should have the necessary machinery implemented. With this, assuming you have a mapping for old to new keys, you can remap the data like this:
std::map<std::string, std::vector> m1;
std::map<int, std::vector> m2;
std::map<std::string, int> keymap;
for (auto i=m1.begin(); i != m1.end(); ++i)
{
m2[keymap[i->first]] = std::move(i->second);
}
Now, all vectors have been moved to a new map leaving the map m1 in an undefined, but destructible state.
If C++11 (as available in VS2010) is not an option, swap the new map with the old one:
std::map<std::string, std::vector> m1;
std::map<int, std::vector> m2;
std::map<std::string, int> keymap;
for (std::map<std::string, std::vector>::iterator i=m1.begin(); i != m1.end(); ++i)
{
m2[keymap[i->first]].swap(i->second);
}

Related

How to use emplace/insert_or_assign in nested map with c++17?

If I have
map<int, map<int, int>> m;
And when I need to insert, I could use
m[1] = { {1, 1} };
But now I want to use emplace/insert_or_assign to replace it, I may use
m.emplace(1, map<int, int>{ {1, 1} });
But I think it is a bit complex to write, expecially I should write map<int, int>again,
even more if the original map change into such as map<int, map<int, map<int, map<int, int>>>>, then I should repeat map<int, int> as much as it need.
So is there any more simple and readable method?
The question should be why do you want to use emplace/insert_or_assign at the first point.
The point of emplace is so that you don't do unnecessary creation until it is needed. With a map<int, map<int, int>>, you might want to create the inner map only if you are going to insert the new map. To properly utilize such behavior, you should only supply the arguments that construct the map, not the map itself.
However, the only constructor map has that can also fill the container during construction is the one taking an initializer_list. So ideally you would want to write something like:
m.emplace(1, {{1, 1}}) // this won't work
However, this wouldn't work because there's no way for emplace to deduce {{1, 1}} as a initializer_list. Which means you have to specify that manually like:
m.emplace(1, std::initializer_list<std::pair<const int, int>>{{1, 1}})
That's quite some writing, which will only be worse if you have more nested layers. At this point it's probably better to just write:
if (m.find(1) == m.end()) {
// or `!m.contains(1)` for C++20 or later
m[1] = {{1, 1}};
}
On the other hand, if you didn't really care about the unnecessary construction, you should just use insert instead:
m.insert({1, {{1, 1}}})
The point of insert_or_assign is to make map works on types that is not default constructible.
When you just call m[1], this will default construct an object if m[1] does not exist. However, that also means operator[] does not work if the type is not default constructible.
However, since a map is default constructible, there isn't much difference between using operator[] directly and using insert_or_assign. So instead, just go use operator[].
You could do
typedef map<int, int> mp;
and then do
m.emplace(1, mp{ {1, 1} });
That's less verbose and also clearly indicates what is map constructor and what isn't.
Hope this helps.
Edit: You can also try doing
template <typename T>
using nest_mp = std::map<int, T>;
typedef std::map<int, int> mp;
and do
m.emplace(1, nest_mp<nest_mp<mp>>>{ {1, { {1, { {1, 1} } } } } };
There are two { braces for each map construction - one to invoke the std::map initializer list constructor, the other to invoke the std::pair initializer list constructor.
It'd be better not to use map<int, map<int, int>> at all. Instead, use map<std::pair<int, int>, int. This means you have a composite key, so instead of storing a value at [A][B] you store it at [pair(A, B)]. This way when you store a value it's always a single memory allocation for its node (because there's just one map).
Then you write code like this:
m.emplace(std::make_pair(1, 1), 1);
m.insert_or_assign({1, 1}, 1);
And if you want to iterate the values with a specific "first" integer in the key:
int subkey = 1;
for (auto it = m.lower_bound({subkey, 0}),
end = m.upper_bound({subkey, INT_MAX});
it != end; ++it)
{
// do something with elements whose key.first == subkey
}

c++ can I new a vector like this in my code into a hashmap

// father is an array of integers, irrelevant to the actual question
vector<pair<int,int>> edges = {
{0, 2},
{2, 3},
{1, 4},
{1, 5}
};
unordered_map<int, vector<pair<int,int>> edges_map_by_component;
for(auto edge: edges){
if(edges_map.find(father[edge.first]) == edges_map_by_component_map.end()){
// ---> my question is, is the following line valid?
edges_map_by_component.emplace(father[edge.first] , new vector<pair<int,int>>());
edges_map[father[edge.first]].push_back(make_pair (edge.first,edge.second));
}
}
In C++,
Can I add a vector object to the hashmap using new like this?
Is that line valid?
If yes, do I need to specify size for vectorif I instantiate it
inside the map?
Edit:
You've said I should not use new here, but seems like simply removing new is not working either. what should I do here. Basically my logic is, if the hashmap does not contain a particular key, I will create a vector of pair<int, int> for it, associate it with that key, and push_back() some pairs into the vector.
You have either a typo or a mistake in your code:
unordered_map<int, vector<pair<int,int>> edges_map_by_component;
// ^^
Should be (given you're using C++11 where you don't need to put spaces between subsequent closing template braces):
unordered_map<int, vector<pair<int,int>>> edges_map_by_component;
// ^^^
Other than that, indeed, remove new, since the second parameter of your map is vector<pair<int, int>>, not a pointer to that. That way it works fine:
#include <iostream>
#include <vector>
#include <unordered_map>
int main(void)
{
std::unordered_map<int, std::vector<std::pair<int,int> > > mymap;
mymap.emplace(5, std::vector<std::pair<int,int> >());
mymap[5].push_back({5, 7});
const std::pair<int, int> p = mymap[5].back();
std::cout << p.first << p.second;
}
The types are shown explicitly for clarity sake. A shorter way to write it would be something along the lines of:
mymap.emplace(5, decltype(mymap)::mapped_type());
One more note is that the emplace operation uses move semantics, so there is no additional copying going on, and you can construct a vector beforehand, fill it with values, and pass to emplace().
Yes usually. No not like you did because you are using the wrong type. What you are inserting here is a pointer to a vector which you haven't declared in your template parameters. You either need to change the template parameter to unsorted_map<int, vector<pair<int,int> >* > or you remove the new. This depends on how you want to use this structure later.

Push_back map into vector

How can I push_back a std::map element into std::vector?
std::vector<std::map<int, int>> v;
// Error
v.push_back(std::make_pair(0, 1))
What cause this error?
Using
v.push_back(std::make_pair(0, 1))
is a problem since a std::pair cannot be converted to a std::map. You can resolve the problem using one of the following methods that I can think of.
Method 1
If you can use a C++11 compiler,
v.push_back({{0, 1}});
Method 2
std::map<int, int> m;
m.insert(std::make_pair(0, 1));
v.push_back(m);
Method 3
std::map<int, int> m;
v.push_back(m);
v.back().insert(std::make_pair(0, 1));
std::vector<std::map<int, int>> v;
means you are declaring vector of maps!
push_back a map into vector, like this
v.push_back({std::make_pair(0, 1)}); //Needs C++11
OR
std::map<int, int> map1;
map1.insert(std::make_pair(0, 1));
v.push_back(map1);
push_back'ng a pair will obviously result into a compilation error.
You can push a map into your Data Structure, std::vector<std::map<int, int>> v. not a pair i.e. element of map.
So, do as following.
std::map<int,int> t;
t.insert(std::make_pair(0, 1));
v.push_back(t);
or in case of C++11
v.push_back({{1,2}});
Or alternatively you can go for emplace_back too.
void emplace_work_around(
std::vector<std::map<int, int>>& v,
std::initializer_list<std::pair<const int,int>> item
)
{
v.emplace_back(item);
}
int main()
{
std::vector<std::map<int, int>> v;
emplace_work_around(v,{{1,2}});
}

Do i need to clear map after assign new map?

Here is an example :
pair< map<int, string>, map<int, string> > test;
test = data;
So do i need to call clear method before assign? Like this :
pair< map<int, string>, map<int, string> > test;
test.first.clear();
test.second.clear();
test = data;
Or first example is correct without memory leaks?
http://www.cplusplus.com/reference/map/map/map/
Map constructor - constructs empty container with no elements.
Pair -Constructs a pair object with its elements value-initialized.
So, my guess is that there is no memory leak, plus you are not allocating any memory there so.

Search in vector<std::pair<int, vector<int> > >

I would like to search within a vector<std::pair<int, vector<int> > >. This won't work due to the vector parameters:
std::vector<std::pair<int, std::vector<int> > > myVec;
iterator = find(myVec.begin(), myVec.end(), i);
Search would be on the first std::pair template parameter (int), in order to get the vector associated with it.
std::vector<std::pair<int, std::vector<int> > > myVec;
This requires C++0x for the lambda expression:
typedef std::pair<int, std::vector<int>> pair_type
std::find_if(myVec.begin(), myVec.end(), [i](pair_type const& pair)
{ return pair.first == i; });
If you're not using C++0x then either roll out your own loop or use something like Boost.Phoenix/Boost.Lambda.
Or, for both cases, why not use std::map?
You could make do with the following (pretty ugly) functoid:
struct FindFirst {
FindFirst(int i) : toFind(i) { }
int toFind;
bool operator()
( const std::pair<int, std::vector<int> > &p ) {
return p.first==toFind;
}
};
using it like this ( I couldn't get bind2nd to work - that's why I used the c'tor ):
int valueToFind = 4;
std::find_if(myVec.begin(), myVec.end(), FindFirst(valueToFind));
I think what you would like is a map:
std::map< int, vector< int > > foo;
You can then add elements, search by key etc:
int key = 4; //This will be the key
vector<int> value(5, 4); //Fill some values (5 4's in this case) into the vector
foo[key]=value; //Adds the pair to the map. Alternatively;
foo.insert( make_pair(key, value) ); //Does the same thing (in this context)
Looking at the way you've done things though, you might be wanting a std::multimap (which allows multiple values to have the same key) Class docs here
You're trying to map an int to a vector of int.
So try map<int, vector<int> >.
The second template parameter of a vector is the allocator - your compiler can probably puzzle out what you wanted to say, the declaration is wrong anyway. What you probably want is some sort of map type, like iammilind suggested.