C++ Sort with Structs

sorting members of structure array

You'll need to implement a sorting function that compares the structs as you require

int compare(const void *s1, const void *s2)
{
struct employee *e1 = (struct employee *)s1;
struct employee *e2 = (struct employee *)s2;
int gendercompare = strcmp(e1->gender, e2->gender);
if (gendercompare == 0) /* same gender so sort by id */
return e1->id - e2->id;
else
return -gendercompare; /* the minus puts "male" first as in the question */
}

And then use qsort from the standard library.

qsort(data, count, sizeof(struct employee), compare);

Inside the compare function you may want to check for id being equal, then you can sort by name (also using strcmp()) however you like.

Edit: Just compiled and fixed this up. Here's a little test program

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

struct employee{
char gender[13];
char name[13];
int id;
};

int compare(const void *s1, const void *s2)
{
struct employee *e1 = (struct employee *)s1;
struct employee *e2 = (struct employee *)s2;
int gendercompare = strcmp(e1->gender, e2->gender);
if (gendercompare == 0) /* same gender so sort by id */
return e1->id - e2->id;
else
return -gendercompare;
}

main()
{
int i;
struct employee info[]={{"male","Matt",1234},{"female","Jessica",2345},{"male","Josh",1235}};

for (i = 0; i < 3; ++i)
printf("%d\t%s\t%s\n", info[i].id, info[i].gender, info[i].name);

qsort(info, 3, sizeof(struct employee), compare);

for (i = 0; i < 3; ++i)
printf("%d\t%s\t%s\n", info[i].id, info[i].gender, info[i].name);
}

With output:

$ ./a.exe
1234 male Matt
2345 female Jessica
1235 male Josh
1234 male Matt
1235 male Josh
2345 female Jessica

Is there a way to sort structs by multiple variables in C?

Use the standard function qsort declared in the header <stdlib.h> and write a user-defined comparison function.

Here you are.

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

#define MAX_USERNAME_LENGTH 10

typedef struct
{
char username[MAX_USERNAME_LENGTH];
unsigned int rides;
unsigned int rank;
} driver;

int cmp( const void *left, const void *right )
{
const driver *a = ( const driver *)left;
const driver *b = ( const driver *)right;

if ( b->rank < a->rank )
{
return -1;
}
else if ( a->rank < b->rank )
{
return 1;
}
else
{
return ( a->rides < b->rides ) - ( b->rides < a->rides );
}
}

int main(void)
{
enum { N = 4 };
driver driver_list[N] =
{
{ "frank209", 3, 6 },
{ "john76", 7, 6 },
{ "harry99", 2, 2 },
{ "bob77", 5, 2 }
};

qsort( driver_list, N, sizeof( driver ), cmp );

for ( size_t i = 0; i < N; i++ )
{
printf( "%s, %u, %u\n",
driver_list[i].username, driver_list[i].rides, driver_list[i].rank );
}

return 0;
}

The program output is

john76, 7, 6
frank209, 3, 6
bob77, 5, 2
harry99, 2, 2

How to sort an array of struct in C?

Just use the standard function qsort declared in the header <stdlib.h>

But before using it you have to define a function that will compare elements of the array.

int cmp( const void *a, const void *b )
{
const giocatore *left = ( const giocatore * )a;
const giocatore *right = ( const giocatore * )b;

return ( right->td < left->td ) - ( left->td < right->td );
}

and then call qsort like

qsort( array, sz, sizeof( giocatore ), cmp );

where array is the name of your array of structures, sz is the size of the array.

If you want for example to sort the array by the data member cognome that points to a string then the comparison function can look even simpler

int cmp( const void *a, const void *b )
{
const giocatore *left = ( const giocatore * )a;
const giocatore *right = ( const giocatore * )b;

return strcmp( left->cognome, right->cognome );
}

Of course the name of the comparison function may be any you like. So you may define several comparison functions with different names as for example sort_by_td, sort_by_cognome and so on.

How to sort array of structs by descending order in C

Use the standard C function qsort declared in the header <stdlib.h> creating an appropriate comparison function.

Here you are.

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

struct create{
char names[30];
int Win;
int Lose;
int Draw;
int Points;
int Average;
int Goals;
};

int cmp( const void *a, const void *b )
{
const struct create *left = a;
const struct create *right = b;

return ( left->Win < right->Win ) - ( right->Win < left->Win );
}

int main(void)
{
struct create a[] =
{
{ .names = "Mike", .Win = 0, .Lose = 1, .Draw = 1 },
{ .names = "Joe", .Win = 2, .Lose = 0, .Draw = 0 },
{ .names = "Bill", .Win = 1, .Lose = 0, .Draw = 1 },
};

const size_t N = sizeof( a ) / sizeof( *a );

for ( size_t i = 0; i < N; i++ )
{
printf( "%-4s %d %d %d\n", a[i].names, a[i].Win, a[i].Lose, a[i].Draw );
}

putchar( '\n' );

qsort( a, N, sizeof( struct create ), cmp );

for ( size_t i = 0; i < N; i++ )
{
printf( "%-4s %d %d %d\n", a[i].names, a[i].Win, a[i].Lose, a[i].Draw );
}

putchar( '\n' );

return 0;
}

The program output is

Mike 0 1 1
Joe 2 0 0
Bill 1 0 1

Joe 2 0 0
Bill 1 0 1
Mike 0 1 1

How to sort an array of structs in C?

You need a structure comparator function that matches the prototype of the function expected by qsort(), viz:

int md_comparator(const void *v1, const void *v2)
{
const mydata *p1 = (mydata *)v1;
const mydata *p2 = (mydata *)v2;
if (p1->id < p2->id)
return -1;
else if (p1->id > p2->id)
return +1;
else
return 0;
}

If you ever get to a more complex sort criterion, this is still a good basis because you can add secondary criteria using the same skeleton:

int md_comparator(const void *v1, const void *v2)
{
const mydata *p1 = (mydata *)v1;
const mydata *p2 = (mydata *)v2;
if (p1->latitude < p2->latitude)
return -1;
else if (p1->latitude > p2->latitude)
return +1;
else if (p1->longitude < p2->longitude)
return -1;
else if (p1->longitude > p2->longitude)
return +1;
else
return 0;
}

Clearly, this repeats for as many criteria as you need. If you need to call a function (strcmp()?) to compare values, call it once but assign the return to a local variable and use that twice:

int md_comparator(const void *v1, const void *v2)
{
const mydata *p1 = (mydata *)v1;
const mydata *p2 = (mydata *)v2;
int rc;
if (p1->latitude < p2->latitude)
return -1;
else if (p1->latitude > p2->latitude)
return +1;
else if (p1->longitude < p2->longitude)
return -1;
else if (p1->longitude > p2->longitude)
return +1;
else if ((rc = strcmp(p1->name_dyn, p2->name_dyn)) < 0)
return -1;
else if (rc > 0)
return +1;
else
return 0;
}

Also, this template works when data members are unsigned integers, and it avoids overflow problems when comparing signed integers. Note that the short cut you might sometimes see, namely variations on:

int md_comparator(const void *v1, const void *v2)   /* BAD */
{ /* BAD */
const mydata *p1 = (mydata *)v1; /* BAD */
const mydata *p2 = (mydata *)v2; /* BAD */
return(p1->id - p2->id); /* BAD */
} /* BAD */

is bad if id is unsigned (the difference of two unsigned integers is never negative), and subject to overflow if the integers are signed and of large magnitude and opposite signs.

How to sort an array** by a struct variable in C?

The qsort function passes a pointer to each element of the array to the comparison function (basically it uses the address-of operator & on each element, as in ®ion[i]).

If each element in the array is a pointer, what is passed is a pointer to a pointer.

That means your comparison function will need to look like

int compareWomenCount(const tTown** village1, const tTown** village2) {
int result = 0;
if ((*village1)->countOfWoman < (*village2)->countOfWoman) {
result = -1;
}
else if ((*village1)->countOfWoman > (*village2)->countOfWoman) {
result = 1;
}
return result;
}

Furthermore, the arguments of the sorting function needs to generic constant pointers (const void *) to be really correct, and then you should cast them to the correct pointer type inside the function.

Sorting array of structs by second field in C

Since the struct has a position member that represents the initial array ordering, you can easily emulate a stable sort. In the comparison function, if the two value members are equal, then return an ordering based on the position members, like this:

int compare(const void *ptr1, const void *ptr2)
{
const struct Map *aptr = ptr1;
const struct Map *bptr = ptr2;

if (aptr->value == bptr->value)
return (aptr->position > bptr->position) - (aptr->position < bptr->position);
else
return (aptr->value > bptr->value) - (aptr->value < bptr->value);
}

Sorting an array of structures using merge sort

Remember as I mentioned in the top comments, your for loops are not initializing i, j, or k

Your sort looks a bit complicated.

There are two ways to do the merge:

(1) copy to temp array and merge back to original (this is what you're doing)

(2) merge from original left/right arrays to temp array and the copy the result back to the original (seems simpler to me)

Anyway, here's a refactored version:

#include <stdio.h>

typedef struct product {
int ident; /* idp of a product */
char desc[64]; /* string that describes a product eg.
"bread" */
int price; /* price of the product */
int weight; /* weight of the product eg. 2kg */
int quant; /* quantity of the product in stock */
int state_prod; /* state of a product, if its 1 its in
the system else is 0 and is not in
the system */
} product;

#define AMAX 28
product a[AMAX];
product aux[AMAX];

void merge_price(product a[], int low, int mid, int high);

void
mergesort_price(product a[], int low, int high)
{
int mid = (high + low) / 2;

if (high <= low)
return;
mergesort_price(a, low, mid);
mergesort_price(a, mid + 1, high);
merge_price(a, low, mid, high);
}

void
merge_price(product a[], int low, int mid, int high)
{
int right;
int left;
int k;
int i;

k = 0;
left = low;
right = mid + 1;

for (; (left <= mid) && (right <= high); ++k) {
if (a[left].price <= a[right].price)
aux[k] = a[left++];
else
aux[k] = a[right++];
}

for (; left <= mid; ++left, ++k)
aux[k] = a[left];

for (; right <= high; ++right, ++k)
aux[k] = a[right];

k = low;
i = 0;
for (; k <= high; ++k, ++i)
a[k] = aux[i];
}

void
show(void)
{
for (int idx = 0; idx < AMAX; ++idx)
printf(" %d",a[idx].price);
printf("\n");
}

int
main(void)
{

for (int idx = 0; idx < AMAX; ++idx)
a[idx].price = AMAX - idx;
show();

mergesort_price(a,0,AMAX - 1);
show();
}

UPDATE:

ok it worked thanks but when i tried to do it but for strings it didnt work. I only changed the if statement really, like can u explain me why it didnt pls.

Okay, I've created a version that generates random product descriptions and sorts by price. It then resorts based on desc [a string sort].

I did this with a pointer to a comparison function that is passed as an extra argument to the existing functions. This is similar to the last argument of libc's qsort function.

This could be extended to a "multikey" sort (e.g. sort by price and then by desc [or whatever struct members you wish]). See: cmpbyboth and cmpbyall.

Note that, at first glance, cmpbyall should be faster than cmpbyboth but with (e.g.) -O2 the compiler may choose to inline the [simple] functions so that both functions may generate exactly the same executable code.

Anyway, here's the code:

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

#define STRMAX 64
#define GENSTR 7

typedef struct product {
int ident; /* idp of a product */
char desc[STRMAX]; /* string that describes a product eg.
"bread" */
int price; /* price of the product */
int weight; /* weight of the product eg. 2kg */
int quant; /* quantity of the product in stock */
int state_prod; /* state of a product, if its 1 its in
the system else is 0 and is not in
the system */
} product;

typedef int (*cmpfnc_p)(const product *,const product *);

#define AMAX 28
product a[AMAX];
product aux[AMAX];

void merge_price(product a[], int low, int mid, int high,cmpfnc_p cmpfnc);

void
mergesort_price(product a[], int low, int high, cmpfnc_p cmpfnc)
{
int mid = (high + low) / 2;

if (high <= low)
return;
mergesort_price(a, low, mid, cmpfnc);
mergesort_price(a, mid + 1, high, cmpfnc);
merge_price(a, low, mid, high, cmpfnc);
}

void
merge_price(product a[], int low, int mid, int high,cmpfnc_p cmpfnc)
{
int cmpflg;
int right;
int left;
int k;
int i;

k = 0;
left = low;
right = mid + 1;

for (; (left <= mid) && (right <= high); ++k) {
cmpflg = cmpfnc(&a[left],&a[right]);
if (cmpflg <= 0)
aux[k] = a[left++];
else
aux[k] = a[right++];
}

for (; left <= mid; ++left, ++k)
aux[k] = a[left];

for (; right <= high; ++right, ++k)
aux[k] = a[right];

k = low;
i = 0;
for (; k <= high; ++k, ++i)
a[k] = aux[i];
}

void
show(const char *tag)
{

printf("\n");
printf("%s",tag);

for (int idx = 0; idx < AMAX; ++idx)
printf(" %s=%d",a[idx].desc,a[idx].price);

printf("\n");
}

// genstr -- get random string
void
genstr(char *desc)
{
int len;
int idx;
int chr;
int carry;

// get random string length
len = (rand() % GENSTR) + 1;

// fill in random string
for (idx = 0; idx < len; ++idx) {
chr = rand() % 26;
chr += 'a';
*desc++ = chr;
}

*desc = 0;
}

// cmpbyprice -- compare by price
int
cmpbyprice(const product *left,const product *right)
{
int cmpflg;

cmpflg = left->price - right->price;

return cmpflg;
}

// cmpbydesc -- compare by description
int
cmpbydesc(const product *left,const product *right)
{
int cmpflg;

cmpflg = strcmp(left->desc,right->desc);

return cmpflg;
}

// cmpbyboth -- compare by price description
int
cmpbyboth(const product *left,const product *right)
{
int cmpflg;

do {
cmpflg = cmpbyprice(left,right);
if (cmpflg)
break;

cmpflg = cmpbydesc(left,right);
if (cmpflg)
break;

// add more if you like ...
} while (0);

return cmpflg;
}

// cmpbyall -- compare by price description [faster version]
int
cmpbyall(const product *left,const product *right)
{
int cmpflg;

do {
cmpflg = left->price - right->price;
if (cmpflg)
break;

cmpflg = strcmp(left->desc,right->desc);
if (cmpflg)
break;

// add more if you like ...
} while (0);

return cmpflg;
}

int
main(int argc,char **argv)
{
unsigned int seed;
product *ptr;

--argc;
++argv;

if (argc > 0)
seed = atoi(*argv);
else
seed = time(NULL);
printf("SEED: %u\n",seed);
srand(seed);

for (int idx = 0; idx < AMAX; ++idx) {
ptr = &a[idx];
ptr->price = AMAX - idx;
genstr(ptr->desc);
}
show("INITED");

mergesort_price(a,0,AMAX - 1,cmpbyprice);
show("BYPRICE");

mergesort_price(a,0,AMAX - 1,cmpbydesc);
show("BYDESC");

mergesort_price(a,0,AMAX - 1,cmpbyboth);
show("BYBOTH");

mergesort_price(a,0,AMAX - 1,cmpbyall);
show("BYALL");
}


Related Topics



Leave a reply



Submit