I've seen other post but I am trying to do this using some of the <algorithm> methods. I have a pointer to a map which contains a key to a vector of pointers to BaseElement classes like the following.
using ElementV = std::vector<std::shared_ptr<BaseElement>>;
using ElementM = std::map<int, ElementV>;
using SElementM = std::shared_ptr<ElementM>;
SElementM elements;
What I am trying to do is concatenate each of the vectors (ElementV) stored as values in the map (ElementM) and populate one large ElementV. I'd like to not have to do deep copies and just access the original elements by their smart pointers (shared_ptr(BaseElement)) from the allElems vector.
The following is wrong, but gives the idea of what I'm trying to do.
ElementV allElems;
for (auto& index : indices) {
allElems = elements->at(index);
}
I suspect I should be using lambas with std::copy, but have been unable to get something similar to the following to work and I think it's because of iterators.
std::copy(allElems.begin(), allElems.end(), [const elements &elems](std::vector<shared_ptr<BaseElement> &val) {
elems ...?
}
Thoughts?
You can get pairs of keys (first) and values (second) via iteraters of std::map, so inserting each vectors via std::for_each is one way.
ElementV allElems;
std::for_each(elements->begin(), elements->end(), [&allElems](const auto& p) {
allElems.insert(allElems.end(), p.second.begin(), p.second.end());
});
You could try this:
ElementV allElems;
//assuming c++17 compiler
for(auto& [ind, elemVectorPtr]: *elements){ //iterate through index, element-vector pointer pair in elements map
//copy across all pointers in element-vector into allElems vector
std::copy(elemVectorPtr->begin(), elemVectorPtr->end(), std::back_inserter(allElems));
}
If you don't have a c++17 compiler, just iterate directly through pairs of items in the map:
for(auto& pair : *elements){
std::copy(pair.second->begin(), pair.second->end(), std::back_inserter(allElems));
}
Related
I wanted to traverse inside a data structure - unordered_map<int, unordered_map<int, unordered_map<int, int>>> myMap. To further specify I want to get the data elements like ->
myMap[someVal1][someVal2]
{all second elements of this unordered map}
I am aware of the fact that the same could by done by a 3d array however using a 3d array would not be efficient as the data range is huge and the program would end up using far more space than required.I tried using some iterators like unordered_map<int, unordered_map<int, unordered_map<int, int>>>::iterator i, and several other such iterators however it always ends up in some error or the other. Could someone help me in understanding how this map can be traversed ? Thanks in advance!
You could traverse the map with a foreach loop (it needs C++11, I think that won't be a problem), if you don't want to use iterators.
myMap mapMapMap;
for(auto& mapMap : mapMapMap){
for(auto& map : mapMap.second){
for(auto& key_value : map.second){
int key = key_value.first;
int value = key_value.second;
// ....
}
}
}
Also, if you didn't want to iterate all the map, but only the values of the third level, given the two first, then this should make it:
int k1, k2;
for(auto& key_value : myMap.at(k1).at(k2)){
//...
}
I want to erase by value from a vector of shared ptr of string (i.e vector<shared_ptr<string>>) . Is there any efficient way of doing this instead of iterating the complete vector and then erasing from the iterator positions.
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<shared_ptr<string>> v;
v.push_back(make_shared<string>("aaa"));
int j = 0,ind;
for(auto i : v) {
if((*i)=="aaa"){
ind = j;
}
j++;
}
v.erase(v.begin()+ind);
}
Also I dont want to use memory for a map ( value vs address).
Try like that (Erase-Remove Idiom):
string s = "aaa";
auto cmp = [s](const shared_ptr<string> &p) { return s == *p; };
v.erase(std::remove_if(v.begin(), v.end(), cmp), v.end());
There is no better way then O(N) - you have to find the object in a vector, and you have to iterate the vector once to find it. Does not really matter if it is a pointer or any object.
The only way to do better is to use a different data structure, which provides O(1) finding/removal. A set is the first thing that comes to mind, but that would indicate your pointers are unique. A second option would be a map, such that multiple pointers pointing to the same value exist at the same hash key.
If you do not want to use a different structure, then you are out of luck. You could have an additional structure hashing the pointers, if you want to retain the vector but also have O(1) access.
For example if you do use a set, and define a proper key - hasher or key_equal. probably hasher is enough defined as the hash for *elementInSet, so each pointer must point to a distinct string for example:
struct myPtrHash {
size_t operator()(const std::shared_ptr<std::string>& p) const {
//Maybe we want to add checks/throw a more meaningful error if p is invalid?
return std::hash<std::string>()(*p);
}
};
such that your set is:
std::unordered_set<std::shared_ptr<std::string>,myPtrHash > pointerSet;
Then erasing would be O(1) simply as:
std::shared_ptr<std::string> toErase = make_shared("aaa");
pointerSet.erase(toErase)
That said, if you must use a vector a more idomatic way to do this is to use remove_if instead of iterating yourself - this will not improve time complexity though, just better practice.
Don't include bits/stdc++.h, and since you're iterating through the hole vector, you should be using std::for_each with a lambda.
Right now I have a map and I need to sort it by value(int), and then by key(string) if there is a tie. I know I would need to write a customized comparison function for this, however so far I haven't been able to make it work.
(I need to store my stuffs in a map since the strings are words and ints are the frequencies and I will need to 'find' the pairs by searching the keys later)
The std::map can only be sorted by key (string in your case).
If you need to sort it by value as well, you'd need to create a std::multimap with the int as key and the string as value, and populate it by iterating over the map.
Alternatively, you could also create a vector<pair<int,string>> that you populate by iteration over the map and just use std::sort().
You can use a std::multiset<std::pair<int, std::string>>
With the information given, it's a bit of a guessing game, but unless you are shuffling massive amounts of data, this may do.
using entry = std::pair<std::string, int>;
using CompareFunc = bool(*)(const entry&, const entry&);
using sortset = std::set<entry, CompareFunc>;
sortset bv(themap.begin(), themap.end(), [](auto& a, auto&b){ a.second!=b.second?a.second<b.second:a.first<b.first; });
for(const auto& d : bv) {
//
}
I have some vector of vectors like this:
vector<vector<Object> > vec;
I also have a struct for the compfunction of std::sort
struct SortByName {
bool operator() (Object& o1, Object& o2) {
return o1.getName() < o2.getName();
}
} _sortByName;
Then I initialize my sort method:
void Object::sort_by_name() {
std::sort(vec.begin(), vec.end(), _sortByName);
}
But this did not work out for me.
So, I have two classes. The one class just fills in objects of the other class inside a vector. Then I am making more vectors of the object and push them into the vector of vectors. And then I want to sort the Objects of the vector inside the big vector by name or something else.
You have a vector that contains vector-s. You have also have a function object that can sort vector-s that contain Object-s. That means your sorting functor can sort vector<Object>-s, not vector<vector<Object>>-s.
You have to write another functor that is able to sort vector<vector<Object>>-s, for example based on their size.
If you have this:
vector<vector<Object>> vec;
You can either sort all the nested vectors:
for(auto &nested_vec : vec)
std::sort(nested_vec .begin(), nested_vec .end(), _sortByName);
or just one specific nested vector:
std::sort(vec[index].begin(), vec[index].end(), _sortByName);
That's simple. However, in case you want to perform sorting in between the nested vectors, it will require a more complex algorithm and you should rather question yourself why you chose to use vector of vectors in the first place.
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);