Accessing Values after Sorting Vector - c++

I sort data elements according to the index of the sorted list. When I need to sort the data a second or third time, I need the original order of the data back each time to sort again. How can I access values stored in original order again?
decltype(m_data) new_m_datalast;
vector<int> m_newOrder;
for(int i=0; i< (int) m_data.size(); ++i)
new_m_datalast.push_back(std::move(m_data[m_newOrder[i]]));
m_data = new_m_datalast;
UPDATE
void Canvas::SetSortedOrder(std::vector<int> idxs)
{
m_newAxesOrder.clear();
m_newAxesOrder = idxs;
m_minmaxdata.clear();
QString filename(":/simdata/minmax.csv");
m_minmax = ReadCSV(filename);
decltype(m_data) new_m_data;
decltype(m_minmaxdata) new_m_minmaxdata;
for(int i=0; i< (int) m_data.size(); ++i)
new_m_data.push_back(std::move(m_data[m_newAxesOrder[i]]));
for(int i=0; i< (int) m_minmaxdata.size(); ++i)
new_m_minmax.push_back(std::move(m_minmax[m_newAxesOrder[i]]));
m_data = new_m_data;
m_minmax = new_m_minmax;
}

If you need to sort a lot of times and access original data later, I suggest you to use tag sort. Here is a small C++ example:
template<typename T>
std::vector<size_t> tag_sort(const std::vector<T>& arr) {
std::vector<size_t> tag(arr.size());
std::iota(tag.begin(), tag.end(), 1);
std::sort(tag.begin(), tag.end(), [&arr](auto a, auto b) { return arr[a] < arr[b]; });
return tag;
}
int main() {
std::vector<int> arr = {5, 7, 2, 5, 9, 6, 1};
auto tag = tag_sort(arr);
for (auto v : tag)
std::cout << v << " ";
std::cout << "\nsorted data:\n";
for (auto v : tag)
std::cout << arr[v];
std::cout << "\n";
}
As you see, you can access original data in arr, and use tag vector to use sorted data.
Tag sort is mainly used when switching elements by moving/copying is very expensive. But I think it is a good approach for your problem too.

I am sure this way isn't the best. But I do it anyway. I make a vector<pair<int,int>> v and I put the data I want to sort in first like: v[i].first = data and their indices in second like:v[i].second = i and I sort the vector or process it as I like. And I can access its original order any time I need.
Note: using sort(v.begin(),v.end()) will sort by first of the pair by default without the need to make a compare function.

Related

Is it possible to compare two element in vector cycle realised as for(int a : vec)

I do have a question.
I want to do a comparison of vector elements. If I write it with an old-style
for(int i = 0; i < vec.size()-1; i++){
if(vec[i] < vec[i+1])
...
}
But with new style I can't understand, how can do the same
for(auto a : vect)
is there any way to do the same job as above?
Thank you
The correct style would be to use the appropriate algorithm for the task at hand. You should use the range-for loop only when you want to do a
for (auto const & i : v) // std::for_each
or a
for (auto & i : v) // std::transform
In this case, if you want to compare adjacent elements, you should use one of the std::adjacent... algorithms.
If you just want to find a position in the vector according to a predicate that compares adjacent elements, use
std::adjacent_find(std::begin(v), std::end(v));
If you want to transform a range according to a predicate that uses adjacent elements, use
std::adjacent_difference(std::begin(v), std::end(v), std::begin(v));
You should use a conventional for-loop only when there isn't an appropriate algorithm you can use (this happens quite rarely).
On a side note, your conventional for-loop has UB if the input range is empty. With -Wall the compiler will point out the problematic code. Prefer to write it like this
for(int i = 0; i < static_cast<int>(vec.size()) - 1; ++i){
if(vec[i] < vec[i+1])
// ...
}
Or like this, if you want to avoid casting
for(auto i = 0u; i + 1 < vec.size(); ++i){
if(vec[i] < vec[i+1])
// ...
}
With range-v3, you might do:
std::vector<int> v {1, 2, -3, 4, 5, -6};
for (auto p : ranges::view::zip(v, v | ranges::view::drop(1))
| ranges::view::filter([](auto p){ return std::get<0>(p) < std::get<1>(p); })) {
std::cout << std::get<0>(p) << " " << std::get<1>(p) << std::endl;
}
Demo
Ranges from C++20 doesn't provide zip unfortunately.

Having trouble creating an array that shows how the indices were moved in another array

This is the gist of the function I'm trying to make. However whenever I print out the order_of_change array its values are always completely off as to where the values of tumor were moved to. I changed the i inside of the if statement to tumor[i] to make sure that tumor[i] was indeed matching its corresponding value in temp_array and it does. Can anyone tell me whats going wrong?
double temp_array[20];
for (int i = 0; i < 20; i++)
{
temp_array[i] = tumor[i];
}
//sort tumor in ascending order
sort(tumor, tumor + 20); //tumor is an array of 20 random numbers
int x = 0; //counter
int order_of_change[20]; //array to house the index change done by sort
while (x < 20) //find where each value was moved to and record it in order_of_change
{
for (int i = 0; i < 20; i++)
{
if (temp_array[x] == tumor[i])
{
order_of_change[x] = i;
x += 1;
}
}
}
To sort the data, but only have the indices show the sort order, all you need to do is create an array of indices in ascending order (starting from 0), and then use that as part of the std::sort criteria.
Here is an example:
#include <algorithm>
#include <iostream>
#include <array>
void test()
{
std::array<double, 8> tumor = {{4, 3, 7, 128,18, 45, 1, 90}};
std::array<int, 8> indices = {0,1,2,3,4,5,6,7};
//sort tumor in ascending order
std::sort(indices.begin(), indices.end(), [&](int n1, int n2)
{ return tumor[n1] < tumor[n2]; });
// output the tumor array using the indices that were sorted
for (size_t i = 0; i < tumor.size(); ++i)
std::cout << tumor[indices[i]] << "\n";
// show the indices
std::cout << "\n\nHere are the indices:\n";
for (size_t i = 0; i < tumor.size(); ++i)
std::cout << indices[i] << "\n";
}
int main()
{ test(); }
Live Example
Even though the example uses std::array, the principle is the same. Sort the index array based on the items in the data. The tumor array stays intact without the actual elements being moved.
This technique can also be used if the items in the array (or std::vector) are expensive to copy if they're moved around, but still want to have the ability to produce a sorted list without actually sorting items.

Erase elements in vector using for loop

How do I use a for loop to erase elements from a vector by its index ? I am getting a vector out of range error. I have a sample code below.
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
for(int i = 0; i < to_erase.size(); i++) {
data.erase(data.begin() + to_erase[i]);
}
I think it is because the size of my vector reduces through every iteration therefore it cannot access index 2.
You would normally employ the erase–remove idiom to delete multiple elements from a vector efficiently (erasing them one by one is generally less efficient, and, as you’ve seen, not always trivial). In its most general form, the idiom looks like this:
data.erase(remove_algorithm(begin(data), end(data)), end(data));
In your case, the remove_algorithm is based off indices in another vector, so we need to provide those as well:
data.erase(
remove_indices(begin(data), end(data), begin(to_erase), end(to_erase)),
end(data));
Unfortunately, such an algorithm isn’t contained in the standard library. However, it’s trivial to write yourself1:
template <typename It, typename It2>
auto remove_indices(It begin, It end, It2 idx_b, It2 idx_e) -> It {
using idx_t = typename std::iterator_traits<It2>::value_type;
std::sort(idx_b, idx_e, std::greater<idx_t>{});
for (; idx_b != idx_e; ++idx_b) {
auto pos = begin + *idx_b;
std::move(std::next(pos), end--, pos);
}
return end;
}
Here, we first sort the indices to be removed from largest to smallest. Next, we loop over those indices. We then (maximally efficiently) move all elements between the current position (to be deleted) and the end of the vector forward by one. Subsequently, the end is decreased by one (to account for the fact that an element got deleted).
Live code
1 *Ahem* Once you’ve removed all the silly typos in your code.
I think it is because the size of my vector reduces through every iteration
Yes!
You could do it by keeping an extra variable, which counts the elements that are deleted, like this:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
int count_removed = 0;
for(unsigned int i = 0; i < to_erase.size(); i++)
data.erase(data.begin() + to_erase[i] - count_removed++);
for(unsigned int i = 0; i < data.size(); ++i)
cout << data[i] << "\n";
return 0;
}
Output:
3
I had the same problem when I first used std::erase(), good question, +1.
Deleting a collection of elements meanwhile you iterate, is unsafe and probalby expensive. I would suggest that each element that meets your criteria gets swapped with an element at the end. (at the end because will be cheaper to erase from the end. You can keep track of how much back you came from the end of the vector (based on the number of swap), and break early our of the loop. Now based on how many elements you swapped you can do something like:
data.resize(data.size() - reverse_counter);
or
int pos = data.size() - reverse_counter;
data.erease(data.begin()+pos, data.end();
It is sudo code just to explain the idea.
As mentioned in the reference, erase not at the end cause re-allocation, which is expensive. Something worth keep in mind:
http://www.cplusplus.com/reference/vector/vector/erase/
I think this is a bad design, because you will change the for loop invariant and will need a lot of workaround to make this happen. Anyway, if you really want to use a for loop, you MAY flag what you whant to delete and run a stl remove_if, something like:
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
using namespace std;
int main() {
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
cout << "Before:\n" ;
for(int i=0; i<data.size(); i++)
cout << i << "\t";
cout << endl;
for(int i=0; i<data.size(); i++)
cout << data[i] << "\t";
cout << endl;
for(int i = 0; i < to_erase.size(); i++) {
//data.erase(data.begin() + to_erase[i]);
data[i] = numeric_limits<int>::max();
}
data.erase(remove_if(data.begin(),
data.end(),
[](int i){return i==numeric_limits<int>::max();}), data.end());
cout << "Next:\n" ;
for(int i=0; i<data.size(); i++)
cout << i << "\t";
cout << endl;
for(int i=0; i<data.size(); i++)
cout << data[i] << "\t";
return 0;
}

Sort a data structure and keep record of the original indices [duplicate]

This question already has answers here:
C++ sorting and keeping track of indexes
(16 answers)
Closed 7 years ago.
Suppose I have code such as the following, to sort elements of a data structure in another data structure, but keep a record of their original indices:
std::vector<int> numbers = {..};
std::vector<std::pair<int, std::vector<int>::size_type>> temp;
for (std::vector<int>::size_type i = 0; i < numbers.size(); i++)
{
temp.push_back({ numbers[i], i });
}
std::sort(temp.begin(), temp.end(), [](const auto& x, const auto& y) { return x.first < y.first; });
So far so good. But what I really want is to store the data and indices in different data structures:
std::vector<int> sorted;
std::vector<std::vector<int>::size_type> indices;
Such that the element at sorted[i] was at index indices[i] in the original data structure.
Other than rolling out my own sorting algorithm, or splitting the data structure after the fact, is there any easy trick using the standard library to do this?
The idea is to store your data in one container and have other containers with indices (based on different sorting criteria).
The idea here is to write function objects that compare two indices based on the sorting criteria and the value in the data container.
The next step is to pass your function object to the appropriate sorting function.
Clarifying Thomas Matthew answer ... Create a vectors of indices 0 to n-1. Use a lambda function to sort the vector of indices according to the data vector. Example code including an optional reorder in place function.
void reorder(std::vector<int>& vA, std::vector<size_t> vI);
int main()
{
std::vector <int> vdata = { 7,2,4,5,3,1,6,0 }; // original data
std::vector <size_t> vindex = { 0,1,2,3,4,5,6,7 }; // original indices
std::sort(vindex.begin(), vindex.end(), [&vdata](size_t i, size_t j)
{return vdata[i] < vdata[j];});
for (size_t i = 0; i < vdata.size(); i++)
std::cout << "[" << vindex[i] << "]=" << vdata[vindex[i]] << " ";
std::cout << std::endl;
reorder(vdata, vindex);
for (size_t i = 0; i < vdata.size(); i++)
std::cout << "[" << i << "]=" << vdata[i] << " ";
return 0;
}
// every move places an element in it's final location
// vI passed by value (copy)
void reorder(std::vector<int>& vA, std::vector<size_t> vI)
{
size_t i, j, k;
int tA;
for (i = 0; i < vA.size(); i++) {
if (i != vI[i]) {
tA = vA[i];
k = i;
while (i != (j = vI[k])) {
vA[k] = vA[j];
vI[k] = k;
k = j;
}
vA[k] = tA;
vI[k] = k;
}
}
}

C++ Sort based on other int array

suppose i have two vector
std::vector<int>vec_int = {4,3,2,1,5};
std::vector<Obj*>vec_obj = {obj1,obj2,obj3,obj4,obj5};
How do we sort vec_obj in regard of sorted vec_int position?
So the goal may look like this:
std::vector<int>vec_int = {1,2,3,4,5};
std::vector<Obj*>vec_obj = {obj4,obj3,obj2,obj1,obj5};
I've been trying create new vec_array:
for (int i = 0; i < vec_int.size(); i++) {
new_vec.push_back(vec_obj[vec_int[i]]);
}
But i think it's not the correct solution. How do we do this? thanks
std library may be the best solution,but i can't find the correct solution to implement std::sort
You don't have to call std::sort, what you need can be done in linear time (provided the indices are from 1 to N and not repeating)
std::vector<Obj*> new_vec(vec_obj.size());
for (size_t i = 0; i < vec_int.size(); ++i) {
new_vec[i] = vec_obj[vec_int[i] - 1];
}
But of course for this solution you need the additional new_vec vector.
If the indices are arbitrary and/or you don't want to allocate another vector, you have to use a different data structure:
typedef pair<int, Obj*> Item;
vector<Item> vec = {{4, obj1}, {3, obj2}, {2, obj3}, {1, obj4}, {5, obj5}};
std::sort(vec.begin(), vec.end(), [](const Item& l, const Item& r) -> bool {return l.first < r.first;});
Maybe there is a better solution, but personally I would use the fact that items in a std::map are automatically sorted by key. This gives the following possibility (untested!)
// The vectors have to be the same size for this to work!
if( vec_int.size() != vec_obj.size() ) { return 0; }
std::vector<int>::const_iterator intIt = vec_int.cbegin();
std::vector<Obj*>::const_iterator objIt = vec_obj.cbegin();
// Create a temporary map
std::map< int, Obj* > sorted_objects;
for(; intIt != vec_int.cend(); ++intIt, ++objIt )
{
sorted_objects[ *intIt ] = *objIt;
}
// Iterating through map will be in order of key
// so this adds the items to the vector in the desired order.
std::vector<Obj*> vec_obj_sorted;
for( std::map< int, Obj* >::const_iterator sortedIt = sorted_objects.cbegin();
sortedIt != sorted_objects.cend(); ++sortedIt )
{
vec_obj_sorted.push_back( sortedIt->second );
}
[Not sure this fits your usecase, but putting the elements into a map will store the elements sorted by key by default.]
Coming to your precise solution if creation of the new vector is the issue you can avoid this using a simple swap trick (like selection sort)
//Place ith element in its place, while swapping to its position the current element.
for (int i = 0; i < vec_int.size(); i++) {
if (vec_obj[i] != vec_obj[vec_int[i])
swap_elements(i,vec_obj[i],vec_obj[vec_int[i]])
}
The generic form of this is known as "reorder according to", which is a variation of cycle sort. Unlike your example, the index vector needs to have the values 0 through size-1, instead of {4,3,2,1,5} it would need to be {3,2,1,0,4} (or else you have to adjust the example code below). The reordering is done by rotating groups of elements according to the "cycles" in the index vector or array. (In my adjusted example there are 3 "cycles", 1st cycle: index[0] = 3, index[3] = 0. 2nd cycle: index[1] = 2, index[2] = 1. 3rd cycle index[4] = 4). The index vector or array is also sorted in the process. A copy of the original index vector or array can be saved if you want to keep the original index vector or array. Example code for reordering vA according to vI in template form:
template <class T>
void reorder(vector<T>& vA, vector<size_t>& vI)
{
size_t i, j, k;
T t;
for(i = 0; i < vA.size(); i++){
if(i != vI[i]){
t = vA[i];
k = i;
while(i != (j = vI[k])){
// every move places a value in it's final location
vA[k] = vA[j];
vI[k] = k;
k = j;
}
vA[k] = t;
vI[k] = k;
}
}
}
Simple still would be to copy vA to another vector vB according to vI:
for(i = 0; i < vA.size(); i++){
vB[i] = vA[vI[i]];