I am stuck on trying to figure out how to insert a vector for a value in a map. For example:
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
map <int, vector<int> > mymap;
mymap.insert(pair<int, vector<int> > (10, #put something here#));
return 0;
}
I don't know what syntax to use insert a vector for value. I tried {1,2}, but that failed. What syntax should I use?
Everything works if I declare a vector in advance and give it a name, but I don't want to do that, as I want to have a map with a lot of vectors.
Thank You in Advance
If you want an empty vector you could do:
mymap.insert(pair<int,vector<int> >(10, vector<int>()));
You could then add whatever elements you want with something like:
mymap[10].push_back(1);
mymap[10].push_back(2);
Edit: Removed incorrect assertion that the vectors would be copied if/when the map grows. As the commenters pointed out, this is not true for std::map, which is node-based.
Basically your question is not about inserting std::vector into a std::map. Your question is how can you easily create an anonymous std::vector with arbitrary initial element values.
In ISO C++03, you can't. C++11 allows using initialization lists for this, however.
If you are stuck with a C++03 compiler, you possibly could create a helper function to return a vector with specified elements:
std::vector<int> make_vector(int a, int b)
{
std::vector<int> v;
v.push_back(a);
v.push_back(b);
return v;
}
If the vectors you're inserting are of different sizes, you could use a variadic function, although doing so would require that you either pass along the number of elements or have a reserved sentinel value.
If you are using C++11 you can use vector's initialization list constructor (the last constructor in that list) which would look like this:
mymap.insert(pair<int, vector<int> > (10, {1, 2, 3}));
If you can only use C++03, vector has a constructor that takes a size and a default value for each element that might be enough for you. Otherwise you will have to construct the vector and then insert it. If you want to avoid an unnessicary copy of the vector when inserting you could swap it in like so:
vector<int> myvec;
myvec.push_back(1);
myvec.push_back(2);
mymap[10].swap(myvec);
This way the vector won't need to be copied. You'll get an extra vector default construction but that's not very expensive.
#put something here# = vector<int>{1,2}
I'm surprised though that {1,2} didn't work. Are you not using a C++11 compiler? If not then you can only create the vector with default constructor there (no values), or fill it with values first and stick it in.
This should work in C++2003 compilers.
#include <iostream>
#include <vector>
#include <map>
#include <cassert>
using namespace std;
std::vector<int> make_vector(int a, int b) {
std::vector<int> result;
result.push_back(a);
result.push_back(b);
return result;
}
int main()
{
map <int, vector<int> > mymap;
mymap.insert(make_pair(10, make_vector(1,2)));
// Or, alternatively:
// mymap[10] = make_vector(1,2);
assert(mymap[10][0] == 1);
assert(mymap[10][1] == 2);
return 0;
}
C++03 does not have initializer lists, which can be a pain to initialize collections.
If you cannot upgrade to a more modern version of the compiler, you can always use the Boost.Assignment library. It has a list_of function precisely for this.
#put something here# -> boost::assign::list_of(1)(2)
Related
I have an unordered_set that stores the following struct
struct match_t{
size_t score;
size_t ci;
};
typedef std::unordered_set<match_t> uniq_t;
Now I want to store the elements of uniq_t myset; to a vector, but in doing so, I want to copy just the score and not the entire struct. I have seen solutions for assigning the elements using assign or back_inserter. I was wondering how to select just the required fields from the struct. I don't see any parameter in assign or back_inserter for this purpose.
Should I try overriding push_back method for the vector or are there other methods for doing this?
EDIT 1
Do I get any performance improvements by using any of these methods instead of looping over the set and assigning the required values?
There is nothing wrong a simple for loop:
std::unordered_set<match_t> myset;
std::vector<std::size_t> myvec;
myvec.reserve(myset.size()); // allocate memory only once
for (const auto& entry : myset)
myvec.push_back(entry.score);
Alternatively, you could use std::transform with a custom lambda:
#include <algorithm>
std::tranform(myset.cbegin(), myset.cend(), std::back_inserter(myvec),
[](const auto& entry){ return entry.score; });
Another way is to use a range library, e.g. with range-v3
#include <range/v3/view/transform.hpp>
std::vector<std::size_t> myvec = myset | ranges::view::transform(&match_t::score);
Performance-wise, you can't do anything about the linear pass over all match_t objects. The important tweak instead is to minimize the number of allocations. As the size of the resulting std::vector is known a priori, a call to std::vector::reserve as shown above makes sure that no unnecessary allocation occur.
I am trying to understand how the std::transform function works, but I'm having a bit of trouble with the following code. I want to take a multiset ms, add 1 to the contents of each element and store them in a new multiset msc. Here is what I have:
int op_increase(int i) { return ++i; }
int main()
{
std::multiset<int> ms = {1,1,2,2,3};
std::multiset<int> msc;
std::transform(ms.begin(), ms.end(), msc.begin(), op_increase);
return 0;
}
However I get the following error:
C3892: _Dest: you cannot assign to a variable that is const
Your code was not utilizing the correct argument to std::transform that allows insertion into an empty container. This requires using an iterator that is intelligent enough to call the appropriate function that calls the container's insert() function.
The solution is to provide std::transform the std::inserter iterator that automatically inserts into the empty multiset. Here is an example:
#include <set>
#include <algorithm>
#include <iterator>
int op_increase(int i) { return ++i; }
int main()
{
std::multiset<int> ms = {1,1,2,2,3};
std::multiset<int> msc;
std::transform(ms.begin(), ms.end(), std::inserter(msc, msc.begin()), op_increase);
// msc now contains 2,2,3,3,4
}
Note the std::inserter is used, and not merely msc.begin(). The inserter will automatically insert the items into the map.
Live Example
The problem here is that std::multiset<T>::begin() returns a std::_Tree_const_iterator type. That's why you cannot change its value. This behavior is sensible: the std::multiset, like std::set, is a sorted container typicaly implemented as a red-black tree, and thus change of a value of one element may require update of the whole data structure. If user really wants to do this, he may erase a node and add it back.
To better understand the std::transform behavior, you may use std::vector container instead of std::multiset. Cplusplus.com contains a good example of code using std::transform.
Also, as far as I understand from your code, you try to add the resulting data into the initially empty std::multiset. To achieve this functionality, you may use std::insert_iterator (Cplusplus.com), like this:
int op_increase(int i) { return ++i; }
int main()
{
std::multiset<int> ms = {1,1,2,2,3};
std::multiset<int> msc;
std::transform(ms.begin(), ms.end(), inserter(msc, msc.begin()), op_increase);
return 0;
}
// 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.
Here I have simple code, it works for std::vector, but don't work for std::list.
Is it wrong because elements in list doesn't alighned?
Edit:
Ok, what is the best way to put list in func? Convert it to vector?
for example I need it when put data in PolyPolyLine
#include <iostream>
#include <vector>
#include <list>
using namespace std;
vector<int> func(int* buf)
{
vector<int> t;
t.push_back(buf[0]);
t.push_back(buf[1]);
return t;
}
int main() {
list<int> ls;
vector<int> v;
ls.push_back(2);
ls.push_back(111111);
v.push_back(12);
v.push_back(11);
vector<int> t1= func(&v[0]);
vector<int> t2= func(&ls.front());
cout<<t1[0]<<t1[1];
cout<<t2[0]<<t2[1];
return 0;
}
The std::list<T> is a linked list, so its memory is not contiguous. You cannot use regular pointers with it to do pointer arithmetic - it's undefined behavior.
If you change your program to take iterators instead, and use std::next to access elements beyond the current one, your program would produce the behavior that you expect.
template <typename T>
vector<int> func(T buf)
{
vector<int> t;
t.push_back(*next(buf, 0));
t.push_back(*next(buf, 1));
return t;
}
...
vector<int> t1= func(v.begin());
vector<int> t2= func(ls.begin());
Demo.
You can't take the address of an item in a list and use it as an array. std::list is a doubly linked list with dynamically allocated nodes. Unlike a vector, the elements in the list are not contiguous.
The only container that guarantees continuous memory allocation is std::vector (and std::array). You don't have any sort of guarantee like that with a std::list, which is why this approach can't possibly work.
I was wondering how I can use the sort function to sort a vector which is private in a class:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class A{
private:
const vector<int> myvec;
A(vector<int>& vec) : myvec(vec) { }
public:
const vector<int>& getvec() { return myvec; }
int get_sec_element(){
int sec_ele = 0;
sort(myvec.begin(), myvec.end());
sec_ele = myvec[2];
return sec_ele;
}
};
So if I created A myvec_object and filled it with a vector which had values already inside it, caliing myvec_object.get_sec_ele() would return the 2nd element in the vector. However, the compiler is giving a huge error message with: "instantiated from here". What could be the problem?
You declared myvec as const -- how would you expect to modify it?
Declare myvec as:
vector<int> myvec;
You have defined your vector as const; this makes it immutable after initialization. If you intend to sort the vector, you'll need to un-const it, or make a copy to sort (which would be slow, of course, if you intend to do this more than once).
You've declared the member variable myvec as const, but std::sort has to modify the vector to sort it. You could:
Make the vector non const by removing the const keyword from its declaration
First make a copy of the vector and sort the copy
replace std::vector with std::multiset, which will keep the items in sorted order to begin with.