Сортировать массив структур по разным членам - PullRequest
0 голосов
/ 05 марта 2020

Мне нужно отсортировать массив структур, каждый из которых имеет члены с разными типами данных, с помощью qsort.

typedef struct {
 char* name;
 int age;
 float wage;
} employee;

Вопрос в том, должен ли я написать 3 разные функции компаратора для каждой из них или есть хороший способ реализовать 1 функцию?

Ответы [ 3 ]

1 голос
/ 05 марта 2020

В любом случае вам нужно написать отдельную функцию для каждого элемента данных, по которой будет сортироваться массив, потому что внутри функции вам нужно сравнивать значения конкретных элементов данных.

Однако вы можете написать общее функция, которая предоставит требуемую функцию сравнения для вызова qsort.

Вот демонстрационная программа.

#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;
}

Выход программы:

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
1 голос
/ 05 марта 2020

Эта функция должна выполнить sh то, что вы ищете:

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;
    }
}

Сначала учитывается алфавитный порядок, затем возраст и, наконец, заработная плата.

Конечно, я предположил, что вы хотел отсортировать элементы в порядке возрастания. Если вы хотите отсортировать их в порядке убывания, все, что вам нужно сделать, это перевернуть значения для каждого сравнения:

float ret2 = emp2->wage - emp1->wage;, чтобы отсортировать сотрудников по заработной плате в порядке убывания, например.

Кроме того, если вы хотите иметь другой приоритет для своей функции сортировки (т. Е. Заработная плата является более решающей, чем алфавитный порядок имен, но последняя все же более решительна, чем возраст), расположите их по-разному в операторах if:

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;
}
1 голос
/ 05 марта 2020

Функция сравнения, подходящая для сортировки объектов типа employee, получит аргументы, указывающие на employee объекты. То, что каждый объект имеет члены нескольких типов, влияет на то, как вы будете реализовывать такую ​​функцию, но это само по себе не дает никаких причин, по которым вам потребуется более одной функции.

int compare_employees(const void *e1, const void *e2) {
    const employee *employee1 = e1;
    const employee *employee2 = e2;

    // ... code to compare *employee1 with *employee2 ...
}

Однако каждая функция сравнения определяет конкретный способ сортировки объектов. Если вы хотите обеспечить сортировку по разным критериям, то вам нужна отдельная функция сравнения для каждого метода заказа. Это не имеет никакого отношения к тому, имеют ли все члены одинаковый тип данных.

...