How can I insert elements into a multimap? - c++

I want to set up a multimap in C++ as follows:
multimap<pair<string, string>, vector<double> > mmList;
But how can I insert data into it? I tried the following code, but it doesn't compile:
mmList.insert(pair<string, string>, vector<double>("a", "b", test));

You can construct pairs using std::make_pair(a, b). Generally you can insert pairs into maps/multimaps. In your case you have to construct a pair consisting of the string pair and the vector:
std::multimap<std::pair<std::string, std::string>, std::vector<double> > mmList;
std::vector<double> vec;
mmList.insert(std::make_pair(std::make_pair("a","b"), vec));

Since C++11 you can use std::multimap::emplace() to get rid of one std::make_pair() compared to harpun's answer:
std::multimap<std::pair<std::string, std::string>, std::vector<double>> mmList;
std::vector<double> test = { 1.1, 2.2, 3.3 };
mmList.emplace(std::make_pair("a", "b"), test);
The code above is no only shorter, but also more efficient, because it reduces the number of unnecessary calls of std::pair constructors.
To further increase efficiency, you can use the piecewise_construct constructor of std::pair, which was introduced specifically for your use case:
mmList.emplace(std::piecewise_construct,
std::forward_as_tuple("a", "b"),
std::forward_as_tuple(test));
This code is no longer shorter, but has the effect that no unnecessary constructors are called. The objects are created directly in the std::multimap from the given arguments.
Code on Ideone

Here is example:
std::multimap<std::string,std::string> Uri_SessionId_Map;
std::string uri = "http";
std::string sessId = "1001";
std::pair<std::string,std::string> myPair(uri,sessId);
Uri_SessionId_Map.insert(myPair);
Just broke up few lines for more readability. You can understand how to make pair. pair must have same templatize form as multimap.

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
}

Is there any way to create an STL map with a composite key and a composite value?

I am trying to create a map, something like: [{11,"Jitendra", 15.5}, {12, "Pranay", 15.5}], where the data between first curly brackets becomes the key and between the second curly brackets becomes values. Whatever technique I am trying it results in error.
Please let me know how I can declare an STL map, insert and manipulate values?
Compound variables are usable as keys for a map as long as they are comparable with operator< (see std::less). The simplest case is using a std::pair:
std::map<std::pair<int, std::string>, int> mymap; // key: int, string; value: int
You can extend the concept with an arbitrary number of key elements by using std::tuple as introduced in C++11.
Both pairs and tuples bring their own comparator overloads (see e.g. for tuple here), so no further work is needed on your side if your tuple elements are comparable by-themselves and are happy with an ordering that gives precedence on the first, then the second, and so on, element of the pair/tuple. Use std::make_pair() or std::make_tuple() to conveniently create them. However, it also works like this (C++17):
std::map<std::tuple<int, int>, std::tuple<int, int>> foo;
// insert
foo[{1,2}] = {3,4};
// access
auto [v1, v2] = foo[{1,2}];
std::cout << v1 << ", " << v2 << std::endl;
To be most flexible, you can extend it even further by using a struct and providing a comparator for the map to order its elements. You can also do this if your compiler lives behind the moon and does not support C++11.

Insert a pair key in map

The way to insert a pair in a map is that:
std::map<char,int> mymap;
// first insert function version (single parameter):
mymap.insert ( std::pair<char,int>('a',100) );
but now I'm trying to insert this in a map:
map<pair<int,int>, int> map1; //(pair is the key and int is a value)
I tried this:
pair<int,int> p;
p.first = 5;
p.second = 20;
map1.insert(pair<int,int>,double> (p,0));
So, how I can do it?
There are so many possibilities for this. You may choose any of the below which suits you better.
Using make_pair
map<pair<int,int>, int> m;
m.insert(make_pair(make_pair(5,20), 0));
Using curly braces
map<pair<int,int>, int> m;
m.insert({{5,20}, 0});
Declaring a C++ Pair first
pair<int,int> p(5,20);
map<pair<int,int>, int> m;
m.insert(make_pair(p, 0));
You can define and fill pairs manually if you want, but it's more common and idiomatic to use make_pair, or (since C++17) the deduction guides for pair:
map1.insert(std::make_pair(std::make_pair(5,20),0));
map1.insert(std::pair{std::pair{5,20},0}); // C++17 or later
First, if you are constructing the key while inserting, you may want to use emplace instead, since it will construct the key and value in place and is also better equipped to forward the arguments passed to it without explicit construction. So your first example would be simplified to
mymap.emplace('a', 100);
while your second example will surely work as
map1.emplace(std::piecewise_construct, std::forward_as_tuple(5, 20), std::forward_as_tuple(0));
and from C++17 onwards may also work as
map1.emplace(5, 20, 0);
See for examples here and here

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.

Why I could use push_back in map but not the back_inserter in set?

The 11th chapter (about associative containers) of C++ Primer 5th says:
The associative containers do not support the sequential-container position-specific operations, such as push_front or back. Because the elements are stored based on their keys, these operations would be meaningless for the associative containers.
However, after I practice two exercises, I am confused:
Given a map<string, vector<string>>, one is asked to add a vector<int> to a given key, my (working) solution is:
using Map = map<string, vector<string>>;
Map my_map;
for(string ln; cin >> ln)
for(string cn; cin >> cn)
my_map[ln].push_back(cn);
However in the second exercise, assuming c is a multiset of strings and v is a vector of strings, is copy(v.begin(), v.end(), back_inserter(c)); legal or illegal?
When I use it, I got this error:
error: 'class std::multiset >' has no member named 'push_back'`
What I have known is back_inserter doesn't work since there's no push_back.
my_map[ln].push_back(cn) does not call push_back on the map (my_map), it calls push_back on the maps mapped_type which is vector<string> - you access this with operator[] (my_map[ln]).
Your statement my_map[ln].push_back(cn) is essentially equivalent to:
vector<string>& v = my_map[ln];
v.push_back(cn);
You do not use push_back on map. You are using it on a vector of strings contained in map.
back_inserter uses push_back internally and associative containers lack such method, so it will not work with it.
You could use std::inserter instead, that will call std::multiset::insert
std::vector<std::string> v {"aaa", "bbbb", "ccc"};
std::multiset<std::string> m;
std::copy(v.begin(), v.end(), std::inserter(m, m.end()));
for (const auto & x : m)
std::cout << x << "\n";
Live on coliru
Your solution doesn't push_back to an associative container, but to a vector stored in that. That's something different.
As the primer says, a multiset doesn't have the sequential push_back method, so you can't use std::copy(...,...,std::back_inserter(..)) with it.