0

I know there are similiar posts to this one but I can't get my head around them so I'm making one too so please don't mark this as a duplicate :)

I've got 2 nested arrays of structs where the outer structs are races and the inner structs are the number of boats from those races. I want to sort the inner struct so that the boats are in ascending order of time_to_complete_race and once I've done this I then assign the first boat in the array points = 4, the second points = 2 and the thirdpoints = 1. I haven't however implemented the points assinging yet so please ignore this part.

struct boat_data {
    int ID;
    int time_to_complete_race;
    int points;
} boat_node;
typedef struct race_result {
    char race_date[80];
    int start_time;
    int num_boats_competing;
    struct boat_data boat_data[MAX_BOAT_NUMBER];
} race_node;

Here is the code that I am using to sort the inner stucts:

void print_races(int races, race_ptr results[], member_node_ptr member) {

    for(int race = 0; race < races; race++) {
        printf("Race Date: %s\n", results[race].race_date);
        printf("Race Start Time: %d\n", results[race].start_time);
        printf("Number of Skippers: %d\n", results[race].num_boats_competing);
        for(int boats = 0; boats < results[race].num_boats_competing; boats++) {
            find_node_by_id(member, results[race].boat_data[boats].ID);
            printf("\tTime to Complete Race in Seconds: %d\n",
                    results[race].boat_data[boats].time_to_complete_race);
        }
        printf("\n");
    }
}

void print_sorted_races(int races, race_ptr results[], member_node_ptr member) {

    race_ptr sorted_results[races];
    struct boat_data temp;

    int race, swap, boat;

    for (race = 0; race < races; race++) {
        sorted_results[race] = results[race];
    }

    for (race = 0; race < races; race++) {

        for (boat = 0; boat< (sorted_results[race].num_boats_competing -1); boat++) {
            for (swap = race + 1; swap < sorted_results[race].num_boats_competing; swap++) {
                if (sorted_results[race].boat_data[swap].time_to_complete_race >
                    sorted_results[race].boat_data[boat].time_to_complete_race) {
                    temp = sorted_results[race].boat_data[boat];
                    sorted_results[race].boat_data[boat] = sorted_results[race].boat_data[swap];
                    sorted_results[race].boat_data[swap] = temp;
                }
            }
        }
    }
    print_races(races, results, member);
}
0

1 Answer 1

1

You are making things much harder and much more error prone by trying to roll-your-own sort routine rather than using qsort provided by the C library. With qsort all you need to do is write a compare function that compares elements of the boat_data[] array member of race_node. The compare function prototype is:

 int compare (const void *a, const void *b)

Where all a and b are, are pointers to elements of the array boat_data[]. So within compare you simply need to cast a and b to the correct type (e.g. struct boat_node const *pa = a, *pb = b; or if you complete your typedef on your first struct, simply boat_node const *pa = a, *pb = b;).

Then compare pa->time_to_complete_race and pb->time_to_complete_race returning -1 if pa->time_to_complete_race sorts before pb->time_to_complete_race or 1 if pb->time_to_complete_racesorts before pa->time_to_complete_race, or 0 if they are equal (note: exactly the way strcmp() does)

Your compare function is then:

int compare (const void *a, const void *b)
{
    boat_node const *pa = a, *pb = b;

    return (pa->time_to_complete_race > pb->time_to_complete_race) -
            (pa->time_to_complete_race < pb->time_to_complete_race);
}

note: after completing your typedef, e.g.

typedef struct boat_data {
    int ID;
    int time_to_complete_race;
    int points;
} boat_node;

Then to sort your boat_data[] array which is a member of race_node race , all you do is call:

    qsort (race.boat_data, race.num_boats_competing, 
            sizeof *race.boat_data, compare);

(done!)

New C programmers are often hesitant to use qsort because they don't know how to write the compare function. After you make friends with the fact that a and b are just pointers to elements of whatever you are sorting, you can easily provide a cast to the proper type and then a comparison that tells qsort how you want it sorted.

In this case you simply want the array of boat_data[] sorted by time_to_complete_race. The return (a > b) - (a < b) form is simply a convenient way to avoid potential overflow were you tempted to return a - b; where, e.g., a is a large negative integer and b a large positive integer.

Full Example

#include <stdio.h>
#include <stdlib.h>

#define MAX_BOAT_NUMBER 10

typedef struct boat_data {
    int ID;
    int time_to_complete_race;
    int points;
} boat_node;

typedef struct race_result {
    char race_date[80];
    int start_time;
    int num_boats_competing;
    boat_node boat_data[MAX_BOAT_NUMBER];
} race_node;

int compare (const void *a, const void *b)
{
    boat_node const *pa = a, *pb = b;

    return (pa->time_to_complete_race > pb->time_to_complete_race) -
            (pa->time_to_complete_race < pb->time_to_complete_race);
}

int main (void) {

    race_node race = { .race_date = "11/26/19",
                       .start_time = 1400,
                       .num_boats_competing = 3,
                       .boat_data = {{ 1, 23, 0 },
                                     { 2, 21, 0 },
                                     { 3, 22, 0 }} };

    qsort (race.boat_data, race.num_boats_competing, 
            sizeof *race.boat_data, compare);

    for (int i = 0; i < race.num_boats_competing; i++)
        printf ("%2d  %4d  %d\n", race.boat_data[i].ID,
                race.boat_data[i].time_to_complete_race,
                race.boat_data[i].points);
}

Example Use/Output

The points members were left all 0:

$ ./bin/boat_race
 2    21  0
 3    22  0
 1    23  0

Much easier than trying to write your own sort. Look things over and let me know if you have further questions.

Sign up to request clarification or add additional context in comments.

3 Comments

This is great thank you! Only issue is that I want to sort them the other way round. Would I just have to change the - to a + in the compare() function?
Nevermind! I've just changed the < operator around in the compare() and that's fixed the problem :)
Yes, sorry for the delay, you can just swap the conditional from > < to < > for a descending sort. Good luck with your coding.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.