Warm tip: This article is reproduced from stackoverflow.com, please click
c++ root-framework sorting

Sort one vector according to another

发布于 2020-04-05 00:25:05

I'm basing my question off the answer to this one:

How to obtain the index permutation after the sorting

I have two std::vectors:

std::vector<int> time={5, 16, 4, 7};   
std::vector<int> amplitude={10,17,8,16};

I want to order the vectors for increasing time, so eventually they will be:

TimeOrdered={4,5,7,16};
AmplitudeOrdered={8,10,16,17};

Once finished, I want to add both ordered vectors to a CERN ROOT TTree. I looked online for solutions and found the example above, where the top answer is to use the following code:

vector<int> data = {5, 16, 4, 7};   
vector<int> index(data.size(), 0);
for (int i = 0 ; i != index.size() ; i++) {
    index[i] = i;
}
sort(index.begin(), index.end(),[&](const int& a, const int& b) {
                return (data[a] < data[b]);
              }
  );
for (int ii = 0 ; ii != index.size() ; ii++) {
  cout << index[ii] << endl;
}

Which I like because it's simple, it doesn't require too many lines and it leaves me with two simple vectors, which I can then use easily for my TTree.

I tried, therefore, to generalise it:

  void TwoVectorSort(){

      std::vector<int> data={5, 16, 4, 7};   
      std::vector<int> data2={10,17,8,16};
      sort(data2.begin(), data2.end(),[&](const int& a, const int& b) {
                        return (data[a] < data[b]);
                      }
        );

      for (int ii = 0 ; ii != data2.size() ; ii++) {
        std::cout <<data[ii]<<"\t"<< data2[ii]<<"\t"<< std::endl;//<<index[ii] 
      }
}

But not only does it not work, it gives me something different each time. I'm running it as a macro with ROOT 6.18/04, using .x TwoVectorSort.cpp+.

Can anyone tell me why it doesn't work and what the simplest solution is? I'm by no means a c++ expert so I hope the answers won't be too technical!

Thanks in advance!

Questioner
Beth Long
Viewed
29
SPM 2020-02-02 09:40

Indeed you can re-use the solution from the link you shared to solve your problem. But you need to keep building the index vector (and I believe there's no need to modify the time or amplitude vectors).

The index vector is used to store the index/position of the time vector values sorted from smallest to largest, so for time={5, 16, 4, 7}:

index[0] will contain the index of the smallest value from time (which is 4, at position 2), hence index[0]=2

index[1] will contain the index of the 2nd smallest value from time (which is 5, at position 0), hence index[1]=0

etc.

And since amplitude's order is based on time you can use index[pos] to access both vectors when building your tree:

time[index[pos]] and amplitude[index[pos]]

Code with the corrections:

#include <iostream>
#include <vector>
#include <algorithm>

int  main(){

    std::vector<int> time={5, 16, 4, 7};
    std::vector<int> amplitude={10,17,8,16};
    std::vector<int> index(time.size(), 0);

    for (int i = 0 ; i != index.size() ; i++) {
        index[i] = i;
    }

    sort(index.begin(), index.end(),
         [&](const int& a, const int& b) {
            return (time[a] < time[b]);
          }
    );

    std::cout << "Time \t Ampl \t idx" << std::endl;
    for (int ii = 0 ; ii != index.size() ; ++ii) {
        std::cout << time[index[ii]] << " \t " << amplitude[index[ii]] << " \t " << index[ii] << std::endl;
    }
}

Output:

Time     Ampl    idx
4        8       2
5        10      0
7        16      3
16       17      1

But not only does it not work, it gives me something different each time

That happened because the parameters that the lambda was receiving were from data2={10,17,8,16} and those values were being used as index to access the data vector at return (data[a] < data[b]). It caused some random sorting because it was accessing out of the vector's range and reading garbage from memory (hence the random behavior).