Context: String array Sorting C and qsort with function comparator different
Full code on Compiler Explorer.
char *str[ROWS][COLS] = {{"Russia", "Boxing", "Mens", "Gold"},
{"America", "Cycling", "Mens", "Gold"},
{"New Zealand", "Swimming", "Womens", "Silver"},
{"India", "Badminton", "Mens", "Bronze"}};
qsort(str,ROWS,sizeof(*str),(compare_rows));
static int compare_rows(const void * a,
const void * b) {
const int sort_colmn = 0;
char * x1 = ((char ** ) a)[sort_colmn];
char * x2 = ((char ** ) b)[sort_colmn];
if (x2 == "India") return 1;
if (x1 > x2 && x2 != "India") return 1;
else if (x1 == x2) return 0;
else return -1;
}
I'm trying to replicate the following SQL sort:
select * from (values('Russia'),('America'),('New Zealand'),('India')) cte(a)
order by a='India' desc, a asc;
Desired order:
-------------
| a |
-------------
| India |
| America |
| New Zealand |
| Russia |
-------------
CodePudding user response:
In your compare_rows
function, you are attempting to compare strings by comparing their addresses. You can't do that.
For example, the comparison x2 == "India"
is unlikely to be true, unless x2
points to a 'merged' value of the string literal on the RHS of that comparison. Similar conditions exist for your other pointer comparisons.
To compare the strings pointed to by the given char*
operands, you need to call the strcmp
function:
static int compare_rows(const void* a, const void* b)
{
const int sort_colmn = 0;
// Note the added use of `const` for the pointers and casts ...
const char* x1 = ((char*const*)a)[sort_colmn];
const char* x2 = ((char*const*)b)[sort_colmn];
if (strcmp(x2,"India") == 0) return 1;
if (strcmp(x1,x2) > 0) return 1; // Comparing x2 to "India" here is redundant
else if (strcmp(x1,x2) == 0) return 0;
else return -1;
// A much simpler way to encode the above three lines is just:
// return strcmp(x1, x2);
}
CodePudding user response:
Your 2D array str
is a contiguous collection of (16) memory addresses, each 'ptr' being independent of the other... qsort
operates in chunks, be that integers, pointers or structs.
To use qsort
clearly, first bind your 4 'columns' together into single chunks:
typedef struct {
char *nation;
char *sport;
char *gender;
char *medal;
} res_t;
res_t results[] = {
{"Russia", "Boxing", "Mens", "Gold"},
{"America", "Cycling", "Mens", "Gold"},
{"New Zealand", "Swimming", "Womens", "Silver"},
{"India", "Badminton", "Mens", "Bronze"}
};
Now, results
is an array of only 4 pointers. Now, you can invoke qsort()
.
qsort( results, sizeof(results)/sizeof(results[0]), sizeof( results[0] ), compare );
sizeof( results[0] )
is the size of one element, and
sizeof(results)/sizeof(results[0])
is the (compiler calculated) number of elements ('rows').
Your special-purpose comparison function can be made much simpler (and effective)...
static int compare_rows( const void *a, const void *b ) {
res_t *p1 = *(res_t **)a;
res_t *p2 = *(res_t **)b;
char *alwaysFirst = "India";
if( strcmp( p1->nation, alwaysFirst ) == 0 ) return 1;
if( strcmp( p2->nation, alwaysFirst ) == 0 ) return -1;
return strcmp( p1->nation, p2->nation );
}