Sort vector of vectors of objects - c++

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.

Related

Sorting a Vector of Vector in Cpp

Say I have this vector of vector [[5,10],[2,5],[4,7],[3,9]] and I want to sort it using the sort() method of cpp, such that it becomes this [[5,10],[3,9],[4,7],[2,5]] after sorting. That is I want to sort based on the second index.
Now I have written this code to sort this vector of vector, but it is not working correctly.
static bool compareInterval( vector<vector<int>> &v1, vector<vector<int>> &v2)
{
return (v1[0][1]>v2[0][1]);
}
sort(boxTypes.begin(), boxTypes.end(), compareInterval);
Can anyone tell me where I am going wrong and hwo can I correct it. Thanks in advance.
Your sort could look like
std::sort(boxTypes.begin(), boxTypes.end(), [](auto const& lhs, auto const& rhs) {
return lhs[1] > rhs[1];
});
in other words sorting by the [1] element of each vector and using > to sort in descending order. Note that in the lambda function lhs and rhs are of type const std::vector<int>&.
When your code is sorting vector of vectors then to the boolean function it passes two vectors (not vector of vectors), and compares them to determine if they need to be interchanged, or are they in correct positions relative to each other.
Hence, here you only need to compare 2 vectors (you have tried to compare vector of vectors).
The change you need to make in compareInterval is:
static bool compareInterval( vector<int> &v1, vector<int> &v2)
{
return (v1[1]>v2[1]);
}
Find my testing code below:
#include <bits/stdc++.h>
using namespace std;
static bool compareInterval( vector<int> &v1, vector<int> &v2)
{
return (v1[1]>v2[1]);
}
int main() {
vector<vector<int>> boxTypes = {{5,10},{2,5},{4,7},{3,9}};
sort(boxTypes.begin(), boxTypes.end(), compareInterval);
for(int i=0;i<4;i++)
cout<<boxTypes[i][0]<<" "<<boxTypes[i][1]<<"\n";
}
Range projections will come somewhat handy for this.
ranges::sort algorithm would receive:
just the vector to sort; no iterators to the begin and end.
(optionally) the function you want to use for sorting, greater in this case.
(optionally) the projection: for every element t of the original vector, which happens to be another vector of two elements, get its second element, i.e. t[1], and sort on that.
std::ranges::sort(boxTypes, std::ranges::greater{}, [](auto&& bt) { return bt[1]; });
Note I have only been able to have this compiling on msvc, not on gcc or clang (and with /std:c++latest, not even with /std:c++20; https://godbolt.org/z/9Kqfa9vhx).

How to concatinate multiple vectors of shared_ptr's?

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));
}

Erase by value in a vector of shared pointers

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.

C++ Optimal way to create a sorted array from a sorted array of objects

Is there a better way to create a sorted array of member variable from a sorted array of custom(ie user defined) objects in C++?
Example -
class People{
public:
//can have multiple parameters. What are the options in either case?
unsigned int birth_year;
};
Lets say we have an array of std::vector<People> and we want to get std::vector<unsigned int> sorted by birth year.
We can use one of the many ways to sort the custom object based on the birth_year mentioned in this link - Sorting a vector of custom objects.
Now, if we need to get the sorted birth year vector, we will have to iterate through the sorted People vector and push it on to a new vector of unsigned int.
Is there a faster way to do this(using memory offsets or such)? OR is there a feature in C++11 which we can leverage for this?
Your question is rather under-specified. So here are a few assumptions and possible solutions
Your vector is already sorted by birth date
In that case, if you really want to be fast, just use reinterpret_cast. It is evil, but if your Person-class really just consists of that one member, then it certainly is the fastest thing.
Your Person class has more members than just birth_date
Use transform with a lambda.
std::vector<unsigned int> vec2;
vec2.reserve(vec1.size());
transform(vec1.begin(), vec1.end(),
back_inserter(vec2),
[](const Person& p) { return p.birth_date; });
You just want to read from the new vector
In that case, you might consider using a view (instead of a vector), see for instance https://github.com/ericniebler/range-v3
It's good to separate the values into a pre-allocated array first, that way they'll all be nice and contiguous for std::sort's rearrangements:
std::vector<unsigned> vec2;
vec2.reserve(vec1.size());
for (const auto& v : vec1) { vec2.push_back(v.birth_year); }
std::sort(vec2.begin(), vec2.end());
std::vector::reserve
The answers posted so far create a new vector, copy the values of the birth_year variable into the new vector, and then sort the new vector.
IMO, it would be better to sort the vector of People first by birth_year, and then to copy the birth_year values from the sorted std::vector<People> to a new std::vector<unsigned int>. This would have the same overall performance, but would leave the vector of People sorted, which might be beneficial in some contexts. You can do this by overloading operator< in the Person class, or by providing a custom comparison function, see std::sort. Here is an example:
#include <algorithm>
#include <vector>
class People{
public:
People(unsigned int BY) : birth_year(BY) {}
unsigned int birth_year;
// Overload operator< so that std::sort can compare People
bool operator<(const People& rhs) const {
return birth_year < rhs.birth_year;
}
};
int main() {
std::vector<unsigned int> uint_sorted_vector;
// Example People vector
std::vector<People> people = {4, 2, 9, 3};
// Reserve space for the sorted uint vector
uint_sorted_vector.reserve(people.size());
// Sort the vector of People
std::sort(people.begin(), people.end());
// People vector is sorted, copy birth_year values to uint vector
for (const auto& person : people) {
uint_sorted_vector.push_back(person.birth_year);
}
}
Here is a live demo : Coliru demo

std::sort to sort an array and a list of index?

I have a function that takes two vectors of the same size as parameters :
void mysort(std::vector<double>& data, std::vector<unsigned int>& index)
{
// For example :
// The data vector contains : 9.8 1.2 10.5 -4.3
// The index vector contains : 0 1 2 3
// The goal is to obtain for the data : -4.3 1.2 9.8 10.5
// The goal is to obtain for the index : 3 1 0 2
// Using std::sort and minimizing copies
}
How to solve that problem minimizing the number of required copies ?
An obvious way would be to make a single vector of std::pair<double, unsigned int> and specify the comparator by [](std::pair<double, unsigned int> x, std::pair<double, unsigned int> y){return x.first < y.first;} and then to copy the results in the two original vectors but it would not be efficient.
Note : the signature of the function is fixed, and I cannot pass a single vector of std::pair.
Inside the function, make a vector positions = [0,1,2,3...]
Sort positions with the comparator (int x, int y){return data[x]<data[y];}.
Then iterate over positions , doing result.push_back(index[*it]);
This assumes the values in index can be arbitrary. If it is guaranteed to already be [0,1,2..] as in your example, then you don't to make the positions array, just use index in it's place and skip the last copy.
http://www.boost.org/doc/libs/1_52_0/libs/iterator/doc/index.html#iterator-facade-and-adaptor
Write a iterator over std::pair<double&, signed int&> that actually wraps a pair of iterators into each vector. The only tricky part is making sure that std::sort realizes that the result is a random access iterator.
If you can't use boost, just write the equivalent yourself.
Before doing this, determine if it is worth your bother. A zip, sort and unzip is easier to write, and programmer time can be exchanged for performance in lots of spots: until you konw where it is optimally spent, maybe you should just do a good-enough job and then benchmark where you need to speed things up.
You can use a custom iterator class, which iterates over both vectors in parallel. Its internal members would consist of
Two references (or pointers), one for each vector
An index indicating the current position
The value type of the iterator should be a pair<double, unsigned>. This is because std::sort will not only swap items, but in some cases also temporarily store single values. I wrote more details about this in section 3 of this question.
The reference type has to be some class which again holds references to both vectors and a current index. So you might make the reference type the same as the iterator type, if you are careful. The operator= of the reference type must allow assignment from the value type. And the swap function should be specialized for this reference, to allow swapping such list items in place, by swapping for both lists separately.
You can use a functor class to hold a reference to the value array and use it as the comparator to sort the index array. Then copy the values to a new value array and swap the contents.
struct Comparator
{
Comparator(const std::vector<double> & data) : m_data(data) {}
bool operator()(int left, int right) const { return data[left] < data[right]; }
const std::vector<double> & m_data;
};
void mysort(std::vector<double>& data, std::vector<unsigned int>& index)
{
std::sort(index.begin(), index.end(), Comparator(data));
std::vector<double> result;
result.reserve(data.size());
for (std::vector<int>::iterator it = index.begin(), e = index.end(); it != e; ++it)
result.push_back(data[*it]);
data.swap(result);
}
This should do it:
std::sort(index.begin(), index.end(), [&data](unsigned i1, unsigned i2)->bool
{ return data[i1]<data[i2]; });
std::sort(data.begin(), data.end());