If you have to keep using your existing data structure, which is essentially a std::tuple
of three std::vector
s, using boost::zip_iterator
would seem to be the way to go. A zip_iterator
treats three iterators (two to indices and one to a value) as a single tuple, and you can use a custom comparison function object to sort your data in-place. Alas, boost::zip_iterator
can't be used with std::sort
, as explained in this Q&A, because it can't be written into.
This means that you would have to write your own zip_iterator class that can be used with std::sort
. Note that it is not a trivial exercise, see this Q&A and/or this paper.
It is a lot easier to sort a std::vector
of a std::tuple
. My attempt below uses a std::tuple
of two indices and a value, and stores those entries into a std::vector
. For the sorting, I use a C++14 generic lambda that forwards the two indices into a smaller tuple and compares those lexicographically (i.e. first on row-index, then on column-index) using the library operator<
of std::tuple
.
#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using index = uint32_t;
using value = float;
using sparse_entry = std::tuple<index, index, value>;
using sparse_matrix = std::vector<sparse_entry>;
int main()
{
// sparse 3x3 matrix
auto m = sparse_matrix {
std::make_tuple( 1, 1, -2.2),
std::make_tuple( 1, 0, 42 ),
std::make_tuple( 0, 2, 3.4),
std::make_tuple( 0, 1, 1.7)
};
// sort by row-index, then column-index
std::sort(begin(m), end(m), [](auto const& L, auto const& R) {
return
std::forward_as_tuple(std::get<0>(L), std::get<1>(L)) <
std::forward_as_tuple(std::get<0>(R), std::get<1>(R))
;
});
for (auto const& elem : m)
std::cout << "{ " << std::get<0>(elem) << ", " << std::get<1>(elem) << ", " << std::get<2>(elem) << "}, \n";
}
Live Example.
If your application can use this transformed data layout (and there maybe cache-performance reasons why it can't), then the above code will do the sorting as you want it.
NOTE: as @Casey mentions, you can also use std::tie
instead of std::forward_as_tuple
, but that can bite you when you change sparse_entry
into a full-fledged user-defined class with getters returning by-value.