Using C++, and hopefully the standard library, I want to sort a sequence of samples in ascending order, but I also want to remember the original indexes of the new samples.<
If it's possible, you can build the position array using find function, and then sort the array.
Or maybe you can use a map where the key would be the element, and the values a list of its position in the upcoming arrays (A, B and C)
It depends on later uses of those arrays.
There is another way to solve this, using a map:
vector<double> v = {...}; // input data
map<double, unsigned> m; // mapping from value to its index
for (auto it = v.begin(); it != v.end(); ++it)
m[*it] = it - v.begin();
This will eradicate non-unique elements though. If that's not acceptable, use a multimap:
vector<double> v = {...}; // input data
multimap<double, unsigned> m; // mapping from value to its index
for (auto it = v.begin(); it != v.end(); ++it)
m.insert(make_pair(*it, it - v.begin()));
In order to output the indices, iterate over the map or multimap:
for (auto it = m.begin(); it != m.end(); ++it)
cout << it->second << endl;
Consider using std::multimap
as suggested by @Ulrich Eckhardt. Just that the code could be made even simpler.
Given
std::vector<int> a = {5, 2, 1, 4, 3}; // a: 5 2 1 4 3
To sort in the mean time of insertion
std::multimap<int, std::size_t> mm;
for (std::size_t i = 0; i != a.size(); ++i)
mm.insert({a[i], i});
To retrieve values and original indices
std::vector<int> b;
std::vector<std::size_t> c;
for (const auto & kv : mm) {
b.push_back(kv.first); // b: 1 2 3 4 5
c.push_back(kv.second); // c: 2 1 4 3 0
}
The reason to prefer a std::multimap
to a std::map
is to allow equal values in original vectors. Also please note that, unlike for std::map
, operator[]
is not defined for std::multimap
.
Beautiful solution by @Lukasz Wiklendt! Although in my case I needed something more generic so I modified it a bit:
template <class RAIter, class Compare>
vector<size_t> argSort(RAIter first, RAIter last, Compare comp) {
vector<size_t> idx(last-first);
iota(idx.begin(), idx.end(), 0);
auto idxComp = [&first,comp](size_t i1, size_t i2) {
return comp(first[i1], first[i2]);
};
sort(idx.begin(), idx.end(), idxComp);
return idx;
}
Example: Find indices sorting a vector of strings by length, except for the first element which is a dummy.
vector<string> test = {"dummy", "a", "abc", "ab"};
auto comp = [](const string &a, const string& b) {
return a.length() > b.length();
};
const auto& beginIt = test.begin() + 1;
vector<size_t> ind = argSort(beginIt, test.end(), comp);
for(auto i : ind)
cout << beginIt[i] << endl;
prints:
abc
ab
a
Make a std::pair
in function then sort pair :
generic version :
template< class RandomAccessIterator,class Compare >
auto sort2(RandomAccessIterator begin,RandomAccessIterator end,Compare cmp) ->
std::vector<std::pair<std::uint32_t,RandomAccessIterator>>
{
using valueType=typename std::iterator_traits<RandomAccessIterator>::value_type;
using Pair=std::pair<std::uint32_t,RandomAccessIterator>;
std::vector<Pair> index_pair;
index_pair.reserve(std::distance(begin,end));
for(uint32_t idx=0;begin!=end;++begin,++idx){
index_pair.push_back(Pair(idx,begin));
}
std::sort( index_pair.begin(),index_pair.end(),[&](const Pair& lhs,const Pair& rhs){
return cmp(*lhs.second,*rhs.second);
});
return index_pair;
}
ideone
Are the items in the vector unique? If so, copy the vector, sort one of the copies with STL Sort then you can find which index each item had in the original vector.
If the vector is supposed to handle duplicate items, I think youre better of implementing your own sort routine.