Reputation: 13
sorry if English no good, but I have questions please: I have this array[quantity] that I will sort:
int main() {
int numberOfFruits[] = {3, 5, 2, 4, 1};
//I do sorting algorithm
}
but I also want other array do same thing and match.
char fruits[] = {"apple", "orange", "bannana", "peach", "watermelon"};
So after algrithm it will be:
int numberOfFruits[] = {1, 2, 3, 4, 5};
char fruits[] = {"watermelon", "banana", "apple", "peach", "orange"};
How I do this? Thank you.
Upvotes: 0
Views: 114
Reputation: 10083
I upvoted HAL9000’s answer because it suggests a better data structure than parallel arrays, but it doesn’t actually answer the title question: How do I sort parallel arrays?
OP has presented an example of parallel arrays. I will add my favorite fruits to his:
int fruit_IDs [] = { 3, 5, 2, 4, 1 10, -7 };
char * fruit_names[] = { "apple", "orange", "banana", "peach", "watermellon", "grapefruit", "strawberry" };
Notice how the ID has no relation to any ordering or indexing of the list. It is just another piece of information. Here is another parallel array structure:
char * first_names[] = { "Harry", "Ron", "Hermione", "Severus" };
char * last_names [] = { "Potter", "Weasley", "Granger", "Snape" };
int year_born [] = { 1980, 1980, 1979, 1960 };
So, how do we sort them without breaking their index association?
The simplest would be to use an additional array which is simply used as indices into the parallel arrays. Sticking with the fruits:
int fruit_indexes[NUM_FRUITS] = { 0, 1, 2, 3, 4, 5, 6 };
Now we have a layer of indirection into our parallel arrays. To print the arrays, we might write:
for (int n = 0; n < NUM_FRUITS; n++)
printf( "%d %s\n", fruit_IDs[fruit_indexes[n]], fruit_names[fruit_indexes[n]] );
As you can imagine, if we reorder the elements of the index array, we also reorder how we view the parallel arrays. For example, to see the fruits in reverse order:
int fruit_indexes[NUM_FRUITS] = { 6, 5, 4, 3, 2, 1, 0 };
You should notice right away that this additional array has consequences:
qsort()
with the index array!First, we need a comparitor for the fruit IDs, accessed via indirection through the index array:
int compare_indexed_fruits_by_ID( const void * a, const void * b )
{
// A and b are pointers to elements of the fruit_indices[] array
int ai = *(const int *)a;
int bi = *(const int *)b;
// We use those indices to compare fruit IDs
if (fruit_IDs[ai] < fruit_IDs[bi]) return -1;
if (fruit_IDs[ai] > fruit_IDs[bi]) return 1;
return 0;
}
Comparing by fruit name works the same way, except now we compare names instead of IDs:
int compare_indexed_fruits_by_name( const void * a, const void * b )
{
// A and b are pointers to elements of the fruit_indices[] array
int ai = *(const int *)a;
int bi = *(const int *)b;
// Use those indices to compare fruit names
return strcmp( fruit_names[ai], fruit_names[bi] );
}
The call to QSort is thereafter very simple — pass the correct comparison function and sort the index array:
qsort( fruit_indexes, NUM_FRUITS, sizeof(*fruit_indexes), &compare_indexed_fruits_by_ID );
Printing our fruits will give you:
-7 strawberry
1 watermellon
2 banana
3 apple
4 peach
5 orange
10 grapefruit
We can sort by name just as easily:
qsort( fruit_indexes, NUM_FRUITS, sizeof(*fruit_indexes), &compare_indexed_fruits_by_name );
Producing:
3 apple
2 banana
10 grapefruit
5 orange
4 peach
-7 strawberry
1 watermellon
One of the nice things about the index array is that you can still use it when adding and removing data from other arrays. Remember, to set up static arrays you need a data structure something like this:
#define MAX_NUM_PEOPLE 1000
char * first_names[MAX_NUM_PEOPLE];
char * last_names [MAX_NUM_PEOPLE];
int indices [MAX_NUM_PEOPLE];
int num_people = 0;
To add new data, just push it onto the back of your existing arrays as usual, checking of course that you have room:
if (num_people < MAX_NUM_PEOPLE)
{
first_names[num_people] = new first name;
last_names [num_people] = new last name;
indices [num_people] = num_people;
num_people += 1;
The new element will naturally be out of order, so you will have to sort the index array again.
qsort( indices, ... );
}
To delete an element you need only swap it off the end before sorting the index array again. Don’t forget to check your array bounds!
if (index_to_delete > 0 && index_to_delete < num_people)
{
first_names[indices[index_to_delete]] = first_names[num_people-1];
last_names [indices[index_to_delete]] = last_names [num_people-1];
indices[index_to_delete] = indices[num_people-1];
num_people -= 1;
qsort( indices, ... );
}
This works very much like the static array version, only your arrays are dynamically-allocated.
char ** first_names = NULL;
char ** last_names = NULL;
int * indices = NULL;
int num_people = 0;
...
first_names = malloc( new_num_people * sizeof(*first_names) );
last_names = malloc( new_num_people * sizeof(*last_names) );
indices = malloc( num_num_people * sizeof(*indices) );
if (!first_names || !last_names || !indices)
{
free( indices );
free( last_names );
free( first_names );
fooey();
}
num_people = new_num_people;
Using realloc()
to increase the size of your arrays works as usual as well, with the added complexity that you must check that every array was reallocated successfully.
That index array can be kind of obnoxious. It would be nice to get rid of it after sorting. And, of course, you can!
Create it dynamically before sorting, initializing it to 0, 1, 2, ...
as always, sort using the array intermediary, and then take the time to rearrange the other arrays with the new order.
void sort_fruits_by( void (*comparitor)(const void *, const void *) )
{
int indices[num_fruits];
for (int n = 0; n < num_fruits; n += 1)
indices[n] = n;
qsort( indices, num_fruits, sizeof(*indices), comparitor );
reorder_people_by_index( indices );
}
This rearrange bit is what takes some time and space:
void reorder_people_by_index( int * indices )
{
char * temp_names[num_people];
memcpy( temp_names, first_names, num_people*sizeof(char*) );
for (int n = 0; n < num_people; n += 1)
first_names[indices[n]] = temp_names[n];
memcpy( temp_names, last_names, num_people*sizeof(char*) );
for (int n = 0; n < num_people; n += 1)
last_names[indices[n]] = temp_names[n];
}
For the people we could reuse the char *
temporary, but for fruits we would need both an int
temporary and a char *
— one for each type of parallel array.
Now the parallel arrays themselves are sorted differently. But you lose the sorting-an-array-of-integers-only advantage.
You can see that it really isn’t too difficult to maintain parallel arrays. The grief is in the details — you are maintaining multiple arrays!
This is why having a single array of structured data is ever so much more convenient.
But, if you have to do it with parallel arrays, it can be done.
Upvotes: 1
Reputation: 2188
First, a variable of type char
is not able to hold a string (unless it holds the value 0, then it is an empty string). A string is a sequence of char
, where the last char
has the value 0. Typically a string is stored in an array, or you use a pointer char *
to point to a string that is stored somewhere else. In your case, the name of the fruits are stored in a string literal, so you can use a char *
to that. An since you want an array of strings, you should declare an array of char *
:
char *fruits[] = {.....};
Now to your actual question:
When you have multiple things that belongs together, you can aggregate them together by declaring a struct
:
struct fruit
{
int number;
char *name;
};
And you can define an array of struct fruit
like this:
struct fruit fruits[] =
{
{1, "watermelon"},
{2, "banana"},
{3, "apple"},
{4, "peach"},
{5, "orange"}
}
When you now swap two element of fruits
, you swap both number
and name
together:
/* Swap the i-th and j-th element in fruits */
struct fruit tmp = fruits[i];
fruits[i] = fruits[j];
fruits[j] = tmp;
Upvotes: 2