As already pointed out by immibis:
Notice that sorting your 2D array, is the same as sorting a normal 1D array where the items you're sorting happen to be arrays.
I would like to add that OP is hopefully aware that a 2D array (array of arrays) is not what was exposed by OP.
double **stored_points
is a pointer to double*
and may represent an array of double*
. This is not a compatible type to e.g. double points[][2]
. (There are numerous Q/As in SO concerning this:
SO: Why can't we use double pointer to represent two dimensional arrays?
is actually tagged with c but applies to c++ as well.)
The standard library already provides a ready std::sort()
to sort a variety of containers (including arrays) which can be used in most common cases – the one of OP inclusive:
Sorts the elements in the range [first, last) in ascending order. The order of equal elements is not guaranteed to be preserved.
The granted complexity of std::sort()
is O(N·log(N)) → much better than the complexity of Bubble sort (OP considered to use) which is O(N²).
There are multiple flavors available. For OPs case, a custom comparator is needed as the meaning of ascending shall be changable on request.
Hence,
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp )
is chosen.
Parameters
first, last - the range of elements to sort
comp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns true if the first argument is less than (i.e. is ordered before) the second.
The signature of the comparison function should be equivalent to the following:
bool cmp(const Type1 &a, const Type2 &b);
While the signature does not need to have const &, the function must not modify the objects passed to it and must be able to accept all values of type (possibly const) Type1 and Type2 regardless of value category (thus, Type1 & is not allowed, nor is Type1 unless for Type1 a move is equivalent to a copy (since C++11)).
The types Type1 and Type2 must be such that an object of type RandomIt can be dereferenced and then implicitly converted to both of them.
For double **stored_points
, in first
stored_points
may be passed, in last
stored_points + n
. Thereby, n
is the size of the array. It's not mentioned in OPs exposed code but it's an absolutely necessary value. A pointer can represent an array of any length. I know only two ways to get the length of an array from a pointer: either provide it separately or use a specific value as end marker (like done in C strings with '\0'
).
For the comparator, a function (or functor) with matching signature has to be passed.
In this specific case, it is
bool(double* const &, double* const &)
but (even better)
bool(double*, double*)
will do as well.
This could be a function, a functor (i.e. a class with operator()
), or a lambda (which resembles one of the former). I decided to use a lambda (to keep my code minimal):
[](double *pt1, double *pt2) {
return pt1[0] != pt2[0] // if first elements unequal
? pt1[0] < pt2[0] // return whether first first < second first
: pt1[1] < pt2[1]; // else whether first second < second second
}
This provides a less operator comparing first sub-element, considering second sub-element only if first is equal. This less comparator defines an Order which is needed in std::sort()
to define the meaning of ascending.
To change the order (for sorting with leading y coordinates), just another lambda is used:
[](double *pt1, double *pt2) {
return pt1[1] != pt2[1] // if second elements unequal
? pt1[1] < pt2[1] // return whether first second < second second
: pt1[0] < pt2[0]; // else whether first first < second first
Looks actually quite similar – just the indices have been swapped.
The complete example:
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
// a print function (usable in output streams)
std::string print(double **data, size_t n)
{
std::ostringstream out;
const char *sep = "";
for (size_t i = 0; i < n; ++i) {
out << sep << '(' << data[i][0] << ", " << data[i][1] << ')';
sep = ", ";
}
return out.str();
}
int main()
{
// sample data of OP
double points[][2] = {
{ 1, 5 }, { 2, 2 }, { 1, 1 }, { 1, 3 }
};
const size_t n = sizeof points / sizeof *points; // let compiler determine
// resemble input data of OP
double *stored_points[n];
for (size_t i = 0; i < n; ++i) stored_points[i] = points[i];
// show input data
std::cout
<< "Input data:\n"
<< " " << print(stored_points, n) << '\n';
// sort in ascending order with leading x:
std::sort(stored_points, stored_points + n,
[](double *pt1, double *pt2) {
return pt1[0] != pt2[0] // if first elements unequal
? pt1[0] < pt2[0] // return whether first first < second first
: pt1[1] < pt2[1]; // else whether first second < second second
});
// show result
std::cout
<< "Data sorted by leading x:\n"
<< " " << print(stored_points, n) << '\n';
// sort in ascending order with leading y:
std::sort(stored_points, stored_points + n,
[](double *pt1, double *pt2) {
return pt1[1] != pt2[1] // if second elements unequal
? pt1[1] < pt2[1] // return whether first second < second second
: pt1[0] < pt2[0]; // else whether first first < second first
});
// show result
std::cout
<< "Data sorted by leading y:\n"
<< " " << print(stored_points, n) << '\n';
// done
return 0;
}
Output:
Input data:
(1, 5), (2, 2), (1, 1), (1, 3)
Data sorted by leading x:
(1, 1), (1, 3), (1, 5), (2, 2)
Data sorted by leading y:
(1, 1), (2, 2), (1, 3), (1, 5)
Live Demo on coliru