Paweł Dymowski Paweł Dymowski - 12 days ago 5
C Question

How to sort array of structs by given member in C

I have a struct like that :

struct Car {
int weight;
int price;
int speed;
etc..}


I want to write a function to sort array of this kind of structs.

void sortCars(struct Car table[], int tableSize, ?structMemberParameter?)
{
struct Car temp;
int swapped;
for (int i = 0; i < tableSize; i++)
{
swapped = 0;
for (int j = 0; j < tableSize - 1; j++) {
if (table[j].?structMemberParameter? > table[j + 1].?structMemberParameter?) {
temp = table[j + 1];
table[j + 1] = table[j];
table[j] = temp;
swapped = 1;
}
}
if (swapped == 0) {
break;
}
}
}


What should I put as "?structMemberParameter?"?

Answer

Writing a comparison function that compares such member from two structs and using qsort(). You can see the example in the qsort(3) manual.

This is the answer to the title of the question, the answer to your question is you don't need to pass the member but the comparison function just like qsort() does.

So the if statement becomes

if (compare(&table[j], &table[j + 1]) > 0) ...

and the function signature

typedef int (*cmpfn_type)(const struct Car *const, const struct Car *const);
void sortCars(struct Car table[], int tableSize, cmpfn_type compare);

So you can have multiple comparison functions, for example let's say you want to compare by price, the appropriate one would be

int compare_by_price(const struct Car *const A, const struct Car *const B)
{
    return (int) (A->price - B->price);
}

you should be careful with the type casting though.

The good thing of this approach, is that you can easily rewrite the comparison function to use the qsort() library function.

NOTE: Answering to your comment/question in the comments you could also have an enumerator, like

enum CarProperties {
    Speed, Price // ... AND SO ON
};

and then a sort function like

void sort_car_array(struct Car *const array,
        size_t size, enum CarProperties sort_property);

where you have a switch statement to choose the appropriate callback to pass to a generic sort like say, generic_car_array_sort() like the suggested above or even, directly to qsort() which would be a better solution.

If your intention is to have an API for the struct Car structure you can do it like this and I think it's a very elegant solution. I also recommend making the struct opaque and allowing the caller only to set/get the values via functions.

Comments