Insert a pair key in map - c++

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

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.

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.

Checking for Item in std::vector<std::vector<std::string>> Using std::find

I have the following object
std::vector<std::vector<std::string>> vectorList;
Then I add to this using
std::vector<std::string> vec_tmp;
vec_tmp.push_back(strDRG);
vec_tmp.push_back(strLab);
if (std::find(vectorList.begin(), vectorList.end(), vec_tmp) == vectorList.end())
vectorList.push_back(vec_tmp);
The std::vector<std::string>s contained vectorList are only ever 2-dimensional and there are no duplicates. This works great, but I now only want to check if vectorList contains an item that index zero equal to the current strDrg. In C# I would not even be thinking about this, but this does not seem straight forward using C++. How can I find if a vector exists in vectorList where strDrg already exists in vectorList.at(i)[0]?
Note: I can use boost.
Use find_if with a lambda:
std::find_if(vectorList.begin(), vectorList.end(),
[&strDrg](const std::vector<std::string>& v) { return v[0] == strDrg; });
It seems you don't need the full power of vector for you inner elements. Consider using:
std::vector<std::array<std::string, 2>>
instead.
For doing exactly what you asked, std::find_if with a lambda as #chris proposed in comments is the best:
std::find_if(ob.begin(), ob.end(),
[&](const auto x){return x[0] == strDRG;});
// Replace auto with "decltype(ob[0])&" until
//you have a C++1y compiler. Might need some years.
But if you only ever have exactly two elements, consider using a std::array<...>, a std::pair<...> or a std::tuple<...> instead of the inner vector.
For tuple and pair, you need to access the first element differently:
pair : member first
tuple: use get<0>(x);

How can I insert elements into a multimap?

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.