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!
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 fromtime
(which is4
, at position 2), henceindex[0]=2
index[1]
will contain the index of the 2nd smallest value fromtime
(which is5
, at position 0), henceindex[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]]
andamplitude[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).
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!
(yw!) A simple way (and imprecise, though) would be: lambda expressions are a way to define functions within functions. std::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?.
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 :)
The parameters have to do with std::sort ( sort explained for beginners ). It would be the same if you were using a function (same code but using a function) 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.
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. I hope it helps! Do not hesitate to open separate questions if you have doubts not related to the initial one.