Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort an array of arrays by different indexes in C

Tags:

c

Say I have a set of data points that are represented as an array of arrays of doubles, so

double **data;

Now if I wanted to sort the data by the some field in each of the data points, say the 2nd field, I would write a comparator that would do something like:

int compare_data_second_field(void *a, void *b) {
    double da = ((double *) a)[1];
    double db = ((double *) b)[1];
    if (da < db) return -1;
    else if (da > db) return 1;
    return 0;
}

and then use qsort to sort them by the 2nd field.

My question is how do I generalize this if I don't know before hand which field I want to sort by? Like I may want to sort by the 1st field sometimes and the 5th field sometimes, etc. I would also like it to be thread safe so I don't want to use a global variable to keep track of what field to sort by since multiple of these might be going at once.

In C++ I would just use a custom sorting class and have an instance variable in the class to keep track of what field to sort by. I don't know how to do something like this in C.

like image 396
pjreddie Avatar asked Jul 01 '12 18:07

pjreddie


2 Answers

Actually, there's a pretty neat solution for this with nested functions (which are a GCC extension).
What you can do is make a generic comparator:

int my_comparator(const void* a, const void* b, int n)
{
    double da = ((double*)a)[n];
    double db = ((double*)b)[n];
    return (da > db) ? 1 : ((da < db) ? -1 : 0);  /* Awesome */
}

and a custom sorting function that wraps the original qsort():

void my_qsort(void* base, size_t num, size_t size,
    int (*comparator)(const void *, const void *, int), int field)
{
    /* Internal comperator */
    int my_qsort_comperator(const void* a, const void* b)
    {
        return comparator(a, b, field);
    }

    /* Invoke the base qsort function */
    qsort(base, num, size, my_qsort_comperator);
}

This function behaves just like the original qsort(), except it takes an additional argument field, which indicates the index of the field to sort by.

like image 44
Eitan T Avatar answered Oct 01 '22 14:10

Eitan T


The best way would be to use qsort_r if it is available on your platform. qsort_r accepts an additional argument that is passed to your comparator, so you could use that to pass the field by which you want to sort your data.

If that is not available on your platform, then there really isn't a simple way to do this. You can work around it using global variables, wrapping your data in a struct that would contain information on the sorting field, or rolling your own qsort_r-like function.

like image 139
houbysoft Avatar answered Oct 01 '22 15:10

houbysoft