Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort two vectors in the same way, with criteria that uses only one of the vectors?

Tags:

c++

c++11

How can I sort two vectors in the same way, with criteria that uses only one of the vectors?

For example, suppose I have two vectors of the same size:

vector<MyObject> vectorA; vector<int> vectorB; 

I then sort vectorA using some comparison function. That sorting reordered vectorA. How can I have the same reordering applied to vectorB?


One option is to create a struct:

struct ExampleStruct {     MyObject mo;     int i; }; 

and then sort a vector that contains the contents of vectorA and vectorB zipped up into a single vector:

// vectorC[i] is vectorA[i] and vectorB[i] combined vector<ExampleStruct> vectorC; 

This doesn't seem like an ideal solution. Are there other options, especially in C++11?

like image 556
user2381422 Avatar asked Jun 12 '13 20:06

user2381422


People also ask

How do you sort two vectors?

This type of sorting can be achieved using simple “ sort() ” function. By default the sort function sorts the vector elements on basis of first element of pairs. Case 2 : Sorting the vector elements on the basis of second element of pairs in ascending order.

How do you sort a vector by another vector?

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.

Can sort be used on vectors?

A vector in C++ can be easily sorted in ascending order using the sort() function defined in the algorithm header file. The sort() function sorts a given data structure and does not return anything. The sorting takes place between the two passed iterators or positions.


2 Answers

Finding a sort permutation

Given a std::vector<T> and a comparison for T's, we want to be able to find the permutation you would use if you were to sort the vector using this comparison.

template <typename T, typename Compare> std::vector<std::size_t> sort_permutation(     const std::vector<T>& vec,     Compare& compare) {     std::vector<std::size_t> p(vec.size());     std::iota(p.begin(), p.end(), 0);     std::sort(p.begin(), p.end(),         [&](std::size_t i, std::size_t j){ return compare(vec[i], vec[j]); });     return p; } 

Applying a sort permutation

Given a std::vector<T> and a permutation, we want to be able to build a new std::vector<T> that is reordered according to the permutation.

template <typename T> std::vector<T> apply_permutation(     const std::vector<T>& vec,     const std::vector<std::size_t>& p) {     std::vector<T> sorted_vec(vec.size());     std::transform(p.begin(), p.end(), sorted_vec.begin(),         [&](std::size_t i){ return vec[i]; });     return sorted_vec; } 

You could of course modify apply_permutation to mutate the vector you give it rather than returning a new sorted copy. This approach is still linear time complexity and uses one bit per item in your vector. Theoretically, it's still linear space complexity; but, in practice, when sizeof(T) is large the reduction in memory usage can be dramatic. (See details)

template <typename T> void apply_permutation_in_place(     std::vector<T>& vec,     const std::vector<std::size_t>& p) {     std::vector<bool> done(vec.size());     for (std::size_t i = 0; i < vec.size(); ++i)     {         if (done[i])         {             continue;         }         done[i] = true;         std::size_t prev_j = i;         std::size_t j = p[i];         while (i != j)         {             std::swap(vec[prev_j], vec[j]);             done[j] = true;             prev_j = j;             j = p[j];         }     } } 

Example

vector<MyObject> vectorA; vector<int> vectorB;  auto p = sort_permutation(vectorA,     [](T const& a, T const& b){ /*some comparison*/ });  vectorA = apply_permutation(vectorA, p); vectorB = apply_permutation(vectorB, p); 

Resources

  • std::vector
  • std::iota
  • std::sort
  • std::swap
  • std::transform
like image 149
Timothy Shields Avatar answered Oct 02 '22 18:10

Timothy Shields


With range-v3, it is simple, sort a zip view:

std::vector<MyObject> vectorA = /*..*/; std::vector<int> vectorB = /*..*/;  ranges::v3::sort(ranges::view::zip(vectorA, vectorB)); 

or explicitly use projection:

ranges::v3::sort(ranges::view::zip(vectorA, vectorB),                  std::less<>{},                  [](const auto& t) -> decltype(auto) { return std::get<0>(t); }); 

Demo

like image 45
Jarod42 Avatar answered Oct 02 '22 17:10

Jarod42