C++, Sort One Vector Based On Another One
The best example I've got is that I want to sort Names based on their Score.
vector <string> Names {"Karl", "Martin", "Paul", "Jennie"};
vector <int> Score{45, 5, 14, 24};
So if I sort the score to {5, 14, 24, 45}, the names should also be sorted based on their score.
An alternative to consolidating the names and scores into a single structure is to create an index list and sort that:
std::vector<int> indices(Names.size());
std::iota(indices.begin(), indices.end(), 0);
std::sort(indices.begin(), indices.end(),
[&](int A, int B) -> bool {
return Score[A] < Score[B];
});
Now indices
can be used to index Names
and Scores
in the desired sorted order.
As already suggested in other answers: Combining the name and the score of each individual is likely the simplest solution.
Generically, this can be achieved with what is sometimes referred to as a "zip" operation: Combining two vectors into a vector of pairs - along with a corresponding "unzip".
Implemented generically, this may look as follows:
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <iterator>
// Fill the zipped vector with pairs consisting of the
// corresponding elements of a and b. (This assumes
// that the vectors have equal length)
template <typename A, typename B>
void zip(
const std::vector<A> &a,
const std::vector<B> &b,
std::vector<std::pair<A,B>> &zipped)
{
for(size_t i=0; i<a.size(); ++i)
{
zipped.push_back(std::make_pair(a[i], b[i]));
}
}
// Write the first and second element of the pairs in
// the given zipped vector into a and b. (This assumes
// that the vectors have equal length)
template <typename A, typename B>
void unzip(
const std::vector<std::pair<A, B>> &zipped,
std::vector<A> &a,
std::vector<B> &b)
{
for(size_t i=0; i<a.size(); i++)
{
a[i] = zipped[i].first;
b[i] = zipped[i].second;
}
}
int main(int argc, char* argv[])
{
std::vector<std::string> names {"Karl", "Martin", "Paul", "Jennie"};
std::vector<int> score {45, 5, 14, 24};
// Zip the vectors together
std::vector<std::pair<std::string,int>> zipped;
zip(names, score, zipped);
// Sort the vector of pairs
std::sort(std::begin(zipped), std::end(zipped),
[&](const auto& a, const auto& b)
{
return a.second > b.second;
});
// Write the sorted pairs back to the original vectors
unzip(zipped, names, score);
for(size_t i=0; i<names.size(); i++)
{
std::cout << names[i] << " : " << score[i] << std::endl;
}
return 0;
}
Best way to do this would be to have a struct which combines the names with their scores and have one vector.
struct Person
{
std::string Name;
int Score;
};
Then you can declare your vector:
std::vector<Person> people{ { "Karl", 45 }, { "Martin", 5 }, { "Paul", 14 } };
And sorting it is easy with std::sort
from <algorithm>
:
std::sort(people.begin(), people.end(),
[](const auto& i, const auto& j) { return i.Score < j.Score; } );
Or you can change the lambda if you want to sort in descending order:
std::sort(people.begin(), people.end(),
[](const auto& i, const auto& j) { return i.Score > j.Score; } );
If you cannot merge the data into a vector of pairs or struct with both, you could create a vector of iterators, or the indexes from 0 to size-1. Then sort this using a custom comparator. Finally, create a new vector, populating it using the iterators or indexes.
template<class T1, class A1, class T2, class A2>
std::vector<T1, A1> sort_by(
std::vector<T1,A1> const& vin, std::vector<T2,A2> const& keys
){
std::vector<std::size_t> is;
is.reserve(vin.size());
for (auto&& unused:keys)
is.push_back(is.size());
std::sort(begin(is),end(is),[&](std::size_t l, std::size_t r){
return keys[l]<keys[r];
});
std::vector<T1, A1> r;
r.reserve(vin.size());
for(std::size_t i:is)
r.push_back(vin[i]);
return r;
}