Sort an array of structs by different members - c

I need to sort an array of structs each having members with different data types using qsort.
typedef struct {
char* name;
int age;
float wage;
} employee;
The question is, do I have to write 3 different comparator functions for each of them or is there a nice way to implement 1 function?

In any case you need to write a separate function for each data member by which the array will be sorted because within the function you need to compare values of concrete data members.
However you can write a general function that will supply the required comparison function for a call of qsort.
Here is a demonstrative program.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char* name;
int age;
float wage;
} employee;
int cmp_by_name( const void *a, const void *b )
{
const employee *first = a;
const employee *second = b;
return strcmp( first->name, second->name );
}
int cmp_by_age( const void *a, const void *b )
{
const employee *first = a;
const employee *second = b;
return ( second->age < first->age ) - ( first->age < second->age );
}
int cmp_by_wage( const void *a, const void *b )
{
const employee *first = a;
const employee *second = b;
return ( second->wage < first->wage ) - ( first->wage < second->wage );
}
enum SortBy { ByName, ByAge, ByWage };
int ( *select( enum SortBy sort_by ) )( const void *, const void * )
{
int ( *cmp[] )( const void *, const void * ) =
{
cmp_by_name, cmp_by_age, cmp_by_wage
};
switch ( sort_by )
{
default:
case ByName:
return cmp[ByName];
case ByAge:
return cmp[ByAge];
case ByWage:
return cmp[ByWage];
}
}
int main(void)
{
enum { N = 3 };
employee e[N] =
{
{ "Tom", 18, 3500.0f },
{ "Bob", 26, 4500.0f },
{ "Jim", 28, 4000.0f }
};
for ( size_t i = 0; i < N; i++ )
{
printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
}
putchar( '\n' );
qsort( e, N, sizeof( employee ), select( ByName ) );
for ( size_t i = 0; i < N; i++ )
{
printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
}
putchar( '\n' );
qsort( e, N, sizeof( employee ), select( ByAge ) );
for ( size_t i = 0; i < N; i++ )
{
printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
}
putchar( '\n' );
qsort( e, N, sizeof( employee ), select( ByWage ) );
for ( size_t i = 0; i < N; i++ )
{
printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
}
putchar( '\n' );
return 0;
}
The program output is
Tom, 18, 3500.000000
Bob, 26, 4500.000000
Jim, 28, 4000.000000
Bob, 26, 4500.000000
Jim, 28, 4000.000000
Tom, 18, 3500.000000
Tom, 18, 3500.000000
Bob, 26, 4500.000000
Jim, 28, 4000.000000
Tom, 18, 3500.000000
Jim, 28, 4000.000000
Bob, 26, 4500.000000

This function should accomplish what you seek:
int compare_employees(const void * e1, const void * e2) {
const employee * emp1 = (employee*)e1;
const employee * emp2 = (employee*)e2;
int ret1 = strcmp(emp1->name, emp2->name);
if (ret1 == 0) {
if (emp1->age == emp2->age) {
float ret2 = emp1->wage - emp2->wage;
if (ret2 == 0)
return 0;
else if (ret2 > 0)
return 1;
else
return -1;
} else {
return emp1->age - emp2->age;
}
} else {
return ret1;
}
}
It first takes alphabetical order into account, then age, and finally wage.
Of course, I assumed you wanted to sort the elements in ascending order. If you want to sort them in descending order, all you'd have to do is flip the values for each comparison:
float ret2 = emp2->wage - emp1->wage; to sort employees by wage in descending order, for example.
Moreover, if you want a different priority for your sorting function (i.e. wage is more decisive than the names' alphabetical order, but the latter is still more decisive than age), arrange them differently within the if statements:
if (ret1 == 0) {
int ret2 = strcmp(emp1->name, emp2->name);
if (ret2 == 0)
return emp1->age - emp2->age;
else
return ret2;
} else if (ret1 > 0) {
return 1;
} else {
return -1;
}

A comparison function appropriate for sorting objects of type employee will receive arguments that point to employee objects. That each object has members of several types affects how you would implement such a function, but that does not in itself present any reason why you would need more than one function.
int compare_employees(const void *e1, const void *e2) {
const employee *employee1 = e1;
const employee *employee2 = e2;
// ... code to compare *employee1 with *employee2 ...
}
However, each comparison function defines a particular way of sorting the objects. If you want to provide for sorting on different criteria then you need a different comparison function for each method of ordering. This has nothing in particular to do with whether the members all have the same data type.

Related

Pointers to a struct inside a struct

I have two structures (one_d and two_d).
I have a function that will take struct two_d *smg as input. In main(), I am trying to create such smg so it will return value c increased.
My problem is that, while creating an array of struct two_d smg[2], I am not sure how to put inside information about its values, as it is a pointer to a different struct.
So how do you use pointer to a struct inside a struct? I would like to create struct two_d smg[2] but i dont now how to deal with struct one_d *a field in it
#include <stdio.h>
enum sid
{
DRB,
DRA,
};
struct one_d
{
unsigned int r;
unsigned int *p;
};
struct two_d
{
struct one_d *a;
enum sid z;
};
unsigned int getSmg(struct two_d *smg)
{
unsigned int c = 0;
const struct two_d *sd = NULL;
const struct one_d *ed = NULL;
for (sd = smg; sd->a != NULL; ++sd)
{
for (ed = sd->a; ed->p != NULL; ++ed)
{
if (DRA == sd->z)
{
/*P Increment the clear-state buffer size */
c += 1 + ed->r;
}
}
}
return c;
}
int main(void)
{
unsigned int rVal = 0;
struct two_d smg[2]={
//
// [0].a ={1,0},
// [0].z =DRA,
// [1].a={1,0},
// [1].z =DRA,
};
rVal = getSmg(smg);
printf("Return value is a %d\n", rVal);
printf("Return value is a l");
return( 0 );
}
Well, at least this compiles... I'm not game to run it, though...
For what it's worth...
enum sid { DRB, DRA, DRAwhoCares };
typedef struct {
unsigned int r;
unsigned int *p;
} oneD_t;
typedef struct {
oneD_t *a;
enum sid z;
} twoD_t;
unsigned int getSmg( twoD_t *smg ) {
unsigned int c = 0;
for( twoD_t *sd = smg; sd->a != NULL; +sd++ ) {
for( oneD_t *ed = sd->a; ed->p != NULL; ed++ ) {
if( DRA == sd->z ) {
/*P Increment the clear-state buffer size */
c += 1 + ed->r;
}
}
}
return c;
}
int main( void ) {
oneD_t foo[] = { { 1, NULL }, /* ... */ };
oneD_t bar[] = { { 1, NULL }, /* ... */ };
twoD_t smg[]={
{ foo, DRA, },
{ bar, DRA, },
{ NULL, DRAwhoCares, },
};
unsigned int rVal = getSmg( smg );
printf( "Return value: %u\n", rVal );
return 0; // return is not a function call... No parenthesis...
}

removing duplicated names for array in c

this is what i have to make: c. It should be possible to remove an animal with a specified name. If more animals with the same name exist, it should remove all the animal with the same name.
this is my code:
void deleteAnimalByName(char *animalName, int *nrOfAnimals, ANIMAL *animalArray)
{
for(int i = 0; i < *nrOfAnimals; i ++)
{
if(strcmp((animalArray + i)->Name, animalName) == 0)
{
for(int j = i; j < *nrOfAnimals - 1; j++)
{
animalArray[j] = animalArray[j + 1];
}
(*nrOfAnimals)--;
}
}
}
the outcome after tyring to delete the animals with the same name:
Animals in shelter: 1
Name: ted
Species: Parrot
Age: 1
only one gets deleted, the other one stays. what could cause this?
For starters the function should be declared at least like
size_t deleteAnimalByName( ANIMAL *animalArray, size_t nrOfAnimals, const char *animalName );
And the function can be defined like
size_t deleteAnimalByName( ANIMAL *animalArray, size_t nrOfAnimals, const char *animalName )
{
size_t n = 0;
for ( size_t i = 0; i < nrOfAnimals; i++ )
{
if ( strcmp( animalArray[i].Name, animalName ) != 0 )
{
if ( n != i ) animalArray[n] = animalArray[i];
++n;
}
}
return n;
}
As for your approach then it at least is inefficient because you move all elements of the array one position left after finding an element that need to be deleted.
Here is a demonstrative program
#include <stdio.h>
#include <string.h>
typedef struct ANIMAL
{
char *Name;
} ANIMAL;
size_t deleteAnimalByName( ANIMAL *animalArray, size_t nrOfAnimals, const char *animalName )
{
size_t n = 0;
for ( size_t i = 0; i < nrOfAnimals; i++ )
{
if ( strcmp( animalArray[i].Name, animalName ) != 0 )
{
if ( n != i ) animalArray[n] = animalArray[i];
++n;
}
}
return n;
}
int main(void)
{
ANIMAL animalArray[] =
{
{ "hare" }, { "hare" }, { "fox" }, { "hare" }
};
size_t nrOfAnimals = sizeof( animalArray ) / sizeof( *animalArray );
nrOfAnimals = deleteAnimalByName( animalArray, nrOfAnimals, "hare" );
for ( size_t i = 0; i < nrOfAnimals; i++ )
{
printf( "%s ", animalArray[i].Name );
}
putchar( '\n' );
return 0;
}
The program output is
fox

All max elements and their postition of array

So, for example, I have array: [1, 4, 9, 3, 9]
I need to find all max elements [9, 9] and their index [2, 4]
How can I do this? In C language
int i, pom, max;
max=*gradovi;
for(i=0;i<n;i++) {
if(*(gradovi+i)>max) {
max=*(gradovi+i);
pom=i;
}
if(*(gradovi+i)==max) {
pom=i;
}
}
return pom;
I need postions of all max elemenents, but this print just last
In any case you need a container that will store the indices of the elements with the maximum value.
You can allocate memory dynamically for such a container.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
size_t max_elements( const int a[], size_t n, size_t **result )
{
*result = NULL;
size_t count = 0;
if ( n != 0 )
{
size_t max_i = 0;
count = 1;
for ( size_t i = 1; i < n; i++ )
{
if ( a[max_i] < a[i] )
{
max_i = i;
count = 1;
}
else if ( !( a[i] < a[max_i] ) )
{
++count;
}
}
*result = malloc( count * sizeof( size_t ) );
if ( *result != NULL )
{
for ( size_t i = max_i, j = 0; i < n && j < count; i++ )
{
if ( !( a[i] < a[max_i ] ) ) ( *result )[j++] = i;
}
}
}
return count;
}
int main(void)
{
int a[] = { 1, 4, 9, 3, 9 };
const size_t N = sizeof( a ) / sizeof( *a );
size_t *result = NULL;
size_t count = max_elements( a, N, &result );
for ( size_t i = 0; i < count; i++ )
{
printf( "%zu: %d, ", result[i], a[result[i]] );
}
putchar( '\n' );
free( result );
return 0;
}
Its output is
2: 9, 4: 9,
If the returned value from the function is not equal to 0 but the pointer result was set to NULL then it means that there was a memory allocation error. You can check such a situation.
I'm stupid, it's simple solution:
void maks(int *gradovi, int n){
int i, pom, max;
max=*gradovi;
for(i=1;i<n;i++){
if(*(gradovi+i)>max){
max=*(gradovi+i);
}
if(*(gradovi+i)==max){
pom=i;
printf("Najvise zarazenih je u gradu sa indeksom: %d\n", pom);
}
}
}

Using qsort() with Structs

I just started learning C and I'm still new to it.
In this program I'm working with an array of structs. The structs are:
typedef struct {
int day;
int month;
int year;
} Date;
typedef struct {
int serial_num;
char full_name[15];
Date *pDate;
} Person;
The array is Person *people.
Now I have two arrays of people and birth dates of those people (same indexes):
const char* names[MAX] = { "Sasson_Sassoni", "Pooh", "James_Bond", "Elvis_is_Alive", "Shilgiya", "Cleopatra", "Sissoo_VeSimmhoo" };
const int dates[MAX][COLS] = {
{ 10, 1, 1988 },
{ 12, 12, 1948 },
{ 4, 12, 1970 },
{ 11, 11, 1890 },
{ 11, 11, 1948 },
{ 1, 10, 1213 },
{ 12, 11, 1948 }
};
By using switch case, every time the user types 1 a person from the lists (Name and birthday) is added to the list people. Then if the user types 3, the list people should be sorted by date (oldest to youngest). So I wrote the following two functions:
void sortList(Person **people, int index) {
qsort(*people, index, sizeof(Person), intcmp);
}
int intcmp(const void *a, const void *b) {
Person *one = (Person *)a;
Person *two = (Person *)b;
int year1 = one->pDate->year;
int year2 = two->pDate->year;
int month1 = one->pDate->month;
int month2 = two->pDate->month;
int day1 = one->pDate->day;
int day2 = two->pDate->day;
if (year1 > year2)
return -1;
else if (year2 > year1)
return 1;
if (month1 > month2)
return -1;
else if (month2 > month1)
return 1;
if (day1 > day2)
return -1;
else if (day2 > day1)
return 1;
return 0;
}
But every time I get an error saying:
Exception thrown: read access violation.
one->pDate was nullptr.
Any help?
Thanks!
EDIT:
Further explanation: In order to insert the people to the array one by one, I made a variable called index and every time a person is added the index grows by one. So When calling the function qsort(), index is the number of people in the array. Also MAX=7, COLS=3, LEN=10. The function that adds people to the array is:
void addToList(Person **people, int *index, const char *names[MAX], const int dates[][COLS]) {
people[*index] = (Person *)malloc(sizeof(Person));
people[*index]->serial_num = *index + 1;
strcpy(people[*index]->full_name, names[*index]);
Date *temp = (Date *)malloc(sizeof(Date));
temp->day = dates[*index][0];
temp->month = dates[*index][1];
temp->year = dates[*index][2];
people[*index]->pDate = temp;
printf("%d %s %d/%d/%d \n", people[*index]->serial_num, people[*index]->full_name, people[*index]->pDate->day, people[*index]->pDate->month, people[*index]->pDate->year);
*index = *index + 1;
}
Your mcve is not complete but I think it's because you confuse pointer and struct:
void sortList(Person **people, int index) {
qsort(people, index, sizeof(Person *), intcmp);
// or qsort(people, index, sizeof *people, intcmp);
}
int intcmp(const void *a, const void *b) {
const Person *one = *(const Person **)a;
const Person *two = *(const Person **)b;

scrolling menu ( c code )

I want to create a menu with max 10 line window, and display in this window all items from a xml file ( containing more than 10 items), by scrolling up/down line in window to view all content of file.
Questions: How make this possible in my API (like in ncurses scroll menu):
"If the sub window given for a window is not big enough to show all the items, then the menu will be scrollable. When you are on the last item in the present list, if you send REQ_DOWN_ITEM, it gets translated into REQ_SCR_DLINE and the menu scrolls by one item. You can manually give REQ_SCR_ operations to do scrolling. Let's see how it can be done."
Part of my code for this feature:
static void menu( commands_t *cmd )
{
/* this display only first 10 line */
int i;
char string[ 128 ];
for( i = 0; i < 10; i++ ) {
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i ) );
menu_list_set_text( cmd->menu, i, string );
}
}
Currently, this function can display only the first 10 items from list.
A
B
C
D
E
-----------------
Line 01 | F |
Line 02 | G |
Line 03 | H |
Line 04 | I |
Line 05 | J |
Line 06 | K |
Line 07 | L |
Line 08 | M |
Line 09 | N |
Line 10 | O |
-----------------
P
Q
R
S
T
.......
Z
For this I try to create API capable to set text in menu line from list, move up/down cursor in menu but I don't now how to make to move lines up or down.
typedef struct list_info_s list_info_t;
struct list_info_s
{
int position;
char name[ 50 ];
list_info_t *next;
list_info_t *prev;
};
const list_info_t *list_get_list( list_mgr_t *mgr, int pos)
{
const list_info_t *tmp = mgr->first;
int i;
for (i = 0; tmp && i < pos; i++)
tmp = tmp->next;
return tmp;
}
const char *list_get_name( list_mgr_t *mgr, int position )
{
const list_info_t *list = list_get_list(mgr, position);
if( (list) && (*list->name) ) {
return list->name;
} else {
return 0;
}
}
TO MOVE UP/DOWN:
void commands_handle( commands_t *cmd, int prog_cmd )
{
switch( prog_cmd ) {
case MENU_UP:
cmd->menu_position = (cmd->menu_position + cmd->menu_size - 1) % (cmd->menu_size);
menu( cmd );
break;
case MENU_DOWN:
cmd->menu_position = (cmd->menu_position + 1) % (cmd->menu_size);
menu( cmd );
break;
return;
}
}
MENU:
static void menu( commands_t *cmd )
{
/* this display only first 10 line */
int i;
char string[ 128 ];
for( i = 0; i < 10; i++ ) {
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i ) );
menu_list_set_text( cmd->menu, i, string );
}
}
Windows of menu is for max 10 items lines because xml may contain a large number of items.
What programming options have to display all lines from file to be visible on menu by presing UP/DOWN button?
This is actual API for menu:
#define MENU_MAX 10
struct menu_s
{
char *name;
char text[ MENU_MAX ][ 128 ];
char arguments[ MENU_MAX ][ 128 ];
int commands[ MENU_MAX ];
char back_argument[ 128 ];
int back_command;
int numlines;
int cursor;
int defaultcursor;
};
menu_t *menu_new( const char *name )
{
menu_t *menu = malloc( sizeof( menu_t ) );
if( !menu ) {
return 0;
}
menu->numlines = 0;
menu->cursor = 0;
menu->defaultcursor = 0;
menu->name = strdup( name );
if( !menu->name ) {
free( menu );
return 0;
}
return menu;
}
void menu_delete( menu_t *menu )
{
free( menu->name );
free( menu );
}
void menu_reset_num_lines( menu_t *menu )
{
menu->numlines = 0;
}
void menu_set_text( menu_t *menu, int line, const char *text )
{
snprintf( menu->text[ line ], sizeof( menu->text[ 0 ] ), "%s", text );
if( line >= menu->numlines ) menu->numlines = line + 1;
}
void menu_set_enter_command( menu_t *menu, int line, int command,
const char *argument )
{
menu->commands[ line ] = command;
snprintf( menu->argum#define MENU_MAX 10
struct menu_s
{
char *name;
char text[ MENU_MAX ][ 128 ];
char arguments[ MENU_MAX ][ 128 ];
int commands[ MENU_MAX ];
char back_argument[ 128 ];
int back_command;
int numlines;
int cursor;
int defaultcursor;
};
menu_t *menu_new( const char *name )
{
menu_t *menu = malloc( sizeof( menu_t ) );
if( !menu ) {
return 0;
}
menu->numlines = 0;
menu->cursor = 0;
menu->defaultcursor = 0;
menu->name = strdup( name );
if( !menu->name ) {
free( menu );
return 0;
}
return menu;
}
void menu_delete( menu_t *menu )
{
free( menu->name );
free( menu );
}
void menu_reset_num_lines( menu_t *menu )
{
menu->numlines = 0;
}
void menu_set_text( menu_t *menu, int line, const char *text )
{
snprintf( menu->text[ line ], sizeof( menu->text[ 0 ] ), "%s", text );
if( line >= menu->numlines ) menu->numlines = line + 1;
}
void menu_set_enter_command( menu_t *menu, int line, int command,
const char *argument )
{
menu->commands[ line ] = command;
snprintf( menu->arguments[ line ], sizeof( menu->arguments[ 0 ] ),
"%s", argument );
}
void menu_set_back_command( menu_t *menu, int command,
const char *argument )
{
menu->back_command = command;
snprintf( menu->back_argument, sizeof( menu->back_argument ),
"%s", argument );
}
void menu_set_cursor( menu_t *menu, int cursor )
{
menu->cursor = cursor;
}
const char *menu_get_name( menu_t *menu )
{
return menu->name;
}
int menu_get_num_lines( menu_t *menu )
{
return menu->numlines;
}
const char *menu_get_text( menu_t *menu, int line )
{
return menu->text[ line ];
}
int menu_get_enter_command( menu_t *menu, int line )
{
return menu->commands[ line ];
}
const char *menu_get_enter_argument( menu_t *menu, int line )
{
return menu->arguments[ line ];
}
int menu_get_back_command( menu_t *menu )
{
return menu->back_command;
}
const char *menu_get_back_argument( menu_t *menu )
{
return menu->back_argument;
}
int menu_get_cursor( menu_t *menu )
{
return menu->cursor;
}
int menu_get_default_cursor( menu_t *menu )
{
return menu->defaultcursor;
}
I tried a ugly solution:
static void menu( commands_t *cmd )
{
int i;
for( i = 0; i < 10; i++ ) {
char string[ 128 ];
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i )
menu_list_set_text( cmd->menu, i, string );
int scroll;
for( scroll = 0; scroll < list_size; scroll++ ) {
if( cmd->curmenupos == 10 + scroll ) {
snprintf( string, sizeof (string), "%s", list_get_name( cmd->listmgr, i + scroll + 1 )
menu_list_set_text( cmd->menu, i, string );
}
}
}
}
where "cmd->curmenupos" is command to get menu line position and
"list_size" is number of items from xml
You can simplify your solution somewhat by using an array based implementation of a ring buffer and then only print your menu from the head/tail at the top left corner. Then scrolling simply becomes advancing the head/tail and overwrite the line that is disappearing with the new one. Using an array based implementation should be ok, as your line numbers is constant anyway.
Depending on how slow the IO is, your software may use less IO resources and hence improve performance, by some simple caching of the old lines. Keep in mind not to prematurely optimize This could be done by letting the ring buffer have more entries than lines on your screen, and then only overwrite when scrolling past a certain point.

Resources