Search code examples
c++sortingroot-framework

Sort one vector according to another


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!


Solution

  • 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).