Home > Net >  qsort mulit criteria sort condition in c
qsort mulit criteria sort condition in c

Time:07-25

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 );
}
  •  Tags:  
  • c
  • Related