0

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!

pseyfert
  • 3,263
  • 3
  • 21
  • 47
Beth Long
  • 375
  • 3
  • 24

1 Answers1

1

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

Community
  • 1
  • 1
SPM
  • 405
  • 1
  • 5
  • 16
  • Thank you very much for the help. I'd never seen the syntax for the lambda before, and I've looked at the link you provided but I found it very technical. Would you mind explaining it to me in a really simple way? What exactly does it do and how does it do it? Thanks a lot again! – Beth Long Feb 03 '20 at 09:19
  • (yw!) A simple way (and imprecise, though) would be: lambda expressions are a way to define functions within functions. [std::sort](http://www.cplusplus.com/reference/algorithm/sort/) receives 3 parameters: first 2 are elements from the vector and the 3rd one is a function (aka functor) used to compare the previous 2 elements. With a lambda you can inline the comparison function and avoid creating a function outside. You will find much better explanations about lambdas in [What is a lambda expression in C++11?](https://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11). – SPM Feb 03 '20 at 12:32
  • Ok, that link is really useful thanks! So to try and really understand: The lambda takes all variables currently in scope into its capture list [&], initiates two constant integer variables as arguments (const int& a, const int& b) and returns... what? Isn't time[a] < time[b] a bool? And how does the lambda know what values of a and b to use? Really trying to learn something new! Thanks again :) – Beth Long Feb 04 '20 at 09:20
  • The parameters have to do with std::sort ( [sort explained for beginners](http://www.cplusplus.com/articles/NhA0RXSz) ). It would be the same if you were using a function ([same code but using a function](https://ideone.com/qXW8L0)) because sort will be calling the lambda/function with two parameters each time from the vector. Both the lambda and the custom function need to return a bool because that's what std::sort uses to compare each pair of elements so it can get the whole structure sorted. – SPM Feb 05 '20 at 14:20
  • Please disregard the output of the above code snippet since it is just to illustrate the difference between a sort using lambda and using a function. You can [change the comparison function to be greater than and it will sort the opposite way](https://ideone.com/ZIbTWD). I hope it helps! Do not hesitate to open separate questions if you have doubts not related to the initial one. – SPM Feb 05 '20 at 14:29