Arrange one array and other array do the same - arrays

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.

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;

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?
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?
Index Array
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:
On the plus side, we no longer have to move stuff around in the parallel arrays to reorder them.
On the minus side, we have an additional array to deal with.
Let’s 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
Dynamic data, static arrays
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, ... );
}
Dynamic data, dynamic arrays
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.
Eliminating the index array
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.
Conclusions
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.

Related

Undefined behavior when deleting an element from dynamic array of structs

I have an n sized array of structs dynamically allocated, and each position of the array is an array too, with different sizes for each position (an array of arrays).
I created a function to delete a given array[index] but I'm facing some undefined behavior, for example:
If the array is of size 3, if I delete array[0],I can't access array[1]. This happens with other combinations of indexes too. The only way it works flawlessly is when I delete from end to start.
Here is the code I have:
Structures:
typedef struct point{
char id[5];
char type[5];
char color[10];
int x;
int y;
} Point;
typedef struct {
char lineID[5];
int nPoints;
Point *pt;
}railData;
typedef struct railway {
railData data;
}railway;
This is how the array was created:
headRail = (railway**)calloc(lineNum,sizeof(railway*));
And each Rail:
headRail[i] = (railway*)calloc(pointsNum,sizeof(railway));
These are the functions to delete a rail:
railway **delRail(railway **headRail, int j)
{
int nPts = 0;
if (!headRail)
{
puts(ERRORS[NULLPOINTER]);
return NULL;
}
// Number of rail points on jth rail
nPts = headRail[j]->data.nPoints;
// Free each rail point from jth rail
for (int i = 0; i < nPts; ++i)
{
free(headRail[j][i].data.pt);
}
// Free allocated memory for jth rail
free(headRail[j]);
return headRail;
}
And this is where I call the previous function:
railway **removeRail(railway **headRail)
{
char userID[20];
int index = 0;
// Quit if no rails
if (!headRail)
{
backToMenu("No rails available!");
return NULL;
}
// Get user input
getString("\nRail ID: ",userID,MINLEN,MAXLEN); // MINLEN = 2 MAXLEN = 4
// get index of the asked rail
getRailIndex(headRail,userID,&index);
if (index != NOTFOUND)
{
headRail = delRail(headRail, index);
// Update number of rails in the array (global var)
NUMOFRAILS--;
backToMenu("Rail deleted!\n");
}
else
backToMenu("Rail not found!");
return headRail;
}
So my question is how can I modify my code so that when position i is eliminated, all other indexes are shifted left and the last position, which would be empty, is discarded (something like realloc but for shrinking)
Is what I'm asking doable without changing the array's structure?
When removing element i, do memmove all the data from i+1 to i to the end of the array and then realloc with the size decremented by 1.
Note that arrays in C do not track their size in any way, so you need to pass the size by an external way.
Your data abstraction is strange. I would expect that headRail[j][0].data.nPoints is used to store the number of points inside the headRail[j][0].data structure, yet there you store the count of headRails in the j row headRail[j][<this count>]. I would advise to rewrite the abstraction, have one "object" for the railway and another for hadling two dimensional arrays of railways with dynamic sizes in all directions.
Like:
railway **delRail(railway **headRail, int j)
{
...
// this is strange, it's equal to
// nPts = headRail[j][0].data.nPoints;
// dunno if you mean that,
// or if [j][0].data.nPoints refers to the size of
// headRail[j][0].data.pt or to the size of the whole array
size_t nPts = headRail[j]->data.nPoints;
for (size_t i = 0; i < nPts; ++i) {
free(headRail[j][i].data.pt);
}
free(headRail[j]);
// note that arrays in C does not know how many elements are there in the array
// so you typically pass that along the arguments, like
// railway **delRail(railway **headRail, size_t railcount, int j);
size_t headRailCount = lineNum; // some external knowledge of the size
memmove(&headRail[j], &headRail[j + 1], (headRailCount - j - 1) * sizeof(*headRail));
void *pnt = realloc(headRail, (headRailCount - 1) * sizeof(*headRail));
if (pnt == NULL) return NULL; // that would be strange
headRail = pnt; // note that the previous headRail is no longer valid
--lineNum; // decrement that object where you store the size of the array
return headRail;
}
What about some encapsulation and more structs instead of 2d array? 2d arrays are really a bit of pain for C, what about:
typedef struct {
// stores a single row of rail datas
struct railData_row_s {
// stores a pointer to an array of rail datas
railData *data;
// stores the count of how many datas of rails are stored here
size_t datacnt;
// stores a pointer to an array of rows of rail datas
} *raildatas;
// stores the size of the pointer of rows of rail datas
size_t raildatascnt;
} railway;
The count of mallocs will stay the same, but thinking about data will get simpler. And each pointer that points to an array of data has it's own size tracking variable. An allocation might look like this:
railway *rail_new(size_t lineNum, size_t pointsNum) {
railway *r = calloc(1, sizeof(*r));
if (!r) { return NULL; }
// allocate the memory for rows of raildata
r->raildatascnt = lineNum;
r->raildatas = calloc(r->raildatascnt, sizeof(*r->raildatas));
if (!t->raildatas) { /* error hadnling */ free(r); abort(); }
// for each row of raildata
for (size_t i = 0; i < r->raildatascnt; ++i) {
struct railData_row_s * const row = &r->raildatas[i];
// allocate the memory for the column of raildata
// hah, looks similar to the above?
row->datacnt = pointsNum;
row->data = calloc(row->datacnt, sizeof(*row->data));
if (!row->data) { /* error ahdnling */ abort(); }
}
return r;
}

C data format redoing

I have data stored as
float testdata3[][7] = {
{171032, 0.4448, -0.3032, -0.7655, -1.3428, 13.5803, -73.0743},
{172292, 0.0099, 0.1470, -0.7301, -17.2272, 7.0038, -11.7722},
{173547, 0.0576, 0.1333, -0.8163, -2.7847, -9.5215, 8.1177 },
...
}
where I am only interested in the second, third and fourth indexes. How can I create a function where it would return for example first, second and third index as their own table from the testdata I have.
For example
testdata_x = {0.4448, 0.099, 0.0576, ...}
testdata_y = {-0.3032, 0.1470, 0.1333, ...}
testdata_z = {-0.7655, -0.7301, -0.8163, ...}
Help would be much appreciated. I am trying to read sensor values from test data and im only interested in acceleration values in x, y and z directions.
You didn't post any code so I'm going to be a bit more vague than normal.
If we create an array of 7 elements such as: int arr[7] = {1, 2, 3, 4, 5, 6, 7};
Accessing the 2nd, third, and 4th elements is as simple as arr[1], arr[2], arr[3] respectively.
That should help with gleaning the data from the existing arrays, but as for making your new array. I don't see any reason to use a multidimensional array instead of 3 regular arrays. All you need to do from here is find out the amount of arrays in testData in order to know how much to allocate and you should be on your way.
So you want to transpose a matrix? then just iterate in the reverse order
#include <stdio.h>
int main(void)
{
double arr[][7] = {
{171032, 0.4448, -0.3032, -0.7655, -1.3428, 13.5803, -73.0743},
{172292, 0.0099, 0.1470, -0.7301, -17.2272, 7.0038, -11.7722},
{173547, 0.0576, 0.1333, -0.8163, -2.7847, -9.5215, 8.1177},
};
#define ROWS sizeof arr / sizeof arr[0]
#define COLS sizeof arr[0] / sizeof arr[0][0]
double test[COLS][ROWS]; /* 7 x 3 */
for (size_t i = 0; i < COLS; i++) {
for (size_t j = 0; j < ROWS; j++) {
test[i][j] = arr[j][i];
}
}
return 0;
}
I don't know what you are looking for but here are several approaches starting from easiest one:
1) You can directly assign a value using indexes to the new array
2) If you need function you can allocate new array inside it assign all
values and return it from a function
It's supposed that indexes never change for x,y,z.
#include <stdio.h>
#include <stdlib.h>
enum
{
X,
Y,
Z
};
float testdata3[][7] = {
{171032, 0.4448, -0.3032, -0.7655, -1.3428, 13.5803, -73.0743},
{172292, 0.0099, 0.1470, -0.7301, -17.2272, 7.0038, -11.7722},
{173547, 0.0576, 0.1333, -0.8163, -2.7847, -9.5215, 8.1177 },
};
float *get_dataa(int coordinate)
{
float *data;
switch(coordinate) {
case X:
data = malloc(sizeof(float) * 3);
data[0] = testdata3[0][1];
data[1] = testdata3[1][1];
data[2] = testdata3[2][1];
return data;
default:
return NULL;
}
}
int main(void)
{
/* first way */
float testdata_X[] = { testdata3[0][1], testdata3[1][1], testdata3[2][1] };
printf("Test number 1 for X %0.4f, %0.4f, %0.4f\n", testdata_X[0], testdata_X[1], testdata_X[2]);
/* second */
float *testdata_xX = get_dataa(X);
printf("Test number 2 for X %0.4f, %0.4f, %0.4f\n", testdata_xX[0], testdata_xX[1], testdata_xX[2]);
free(testdata_xX);
return 0;
}
output:
Test number 1 for X 0.4448, 0.0099, 0.0576
Test number 2 for X 0.4448, 0.0099, 0.0576

Storing a char n number of times in 1 loop

Given an array of numbers [2,1,1]
Given an array of chars [x,y,z];
Trying to create a char array that looks like [x,x,y,z] // put in char times its number in first array. so #index 0 char is x and value is 2 so put x x
I can do this using 2 loops but is it possible to do it only using 1 loop? to make things less complex?
One can possibly, do this in one loop. I have tried to implement that, I hope I had done that in the rightful sense.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int count = 0, i = 0, j = 0;
int numbers[] = {4, 3, 2};
char array[] = {'x', 'y', 'z'};
int size = sizeof(numbers) / sizeof(numbers[0]);
char target[BUFSIZ] = {'\0'};
for (i = 0; i < size; )
{
target[j++] = array[i];
++count;
if (!(numbers[i] > count))
{
++i;
count = 0;
}
}
printf("target: %s\n", target);
return EXIT_SUCCESS;
}
Here is the output:
C:\Mine\C\test>build example
"Turbo C Compiler"
Turbo C++ Version 3.00 Copyright (c) 1992 Borland International
source\example.c:
Turbo Link Version 5.0 Copyright (c) 1992 Borland International
Available memory 4125804
target: xxxxyyyzz
"GCC Compiler"
target: xxxxyyyzz
Press any key to continue . . .
The short answer is that you need two loops - one for the char array, and one for each entry in the number array.
Say that n_array is the number array, and c_array is the character array and array is the final array.
int idx=0;
for (int cidx=0; cidx<C_LEN; cidx++)
for (int nidx=0; nidx<n_array[cidx]; nidx++)
array[idx++] = c_array[cidx];
As pointed out in the comments, you might need to allocate array as well. The only way to do that accurately is to either count the number of entries you will need by summing the values in n_array, or by starting with the length of c_array and using realloc as necessary.
Here's what the code looks like when implemented as a single loop. As you can see by comparing with #Trenin's answer, the nested loop solution is actually the simpler solution.
int main( void )
{
int numberArray[] = { 5, 10, 2 };
int charArray[] = { 'x', 'y', 'z' };
int outputArray[200];
int inputIndex = 0;
int inputLength = sizeof(numberArray) / sizeof(numberArray[0]);
int outputIndex = 0;
int outputCount = 0;
while ( inputIndex < inputLength )
{
if ( outputCount < numberArray[inputIndex] )
{
outputArray[outputIndex++] = charArray[inputIndex];
outputCount++;
}
else
{
outputCount = 0;
inputIndex++;
}
}
}

Strange values being initialized into array

int letters[] = {['A'] = 4, ['B'] = 8, ['E'] = 3, ['I'] = 1, ['O'] = 0, ['S'] = 5};
When I initialize the array letters above, my assumption is that at the index of each capital letter, the value will be the number i.e. if ['A'] = 4, then at index 'A' the value will be 4 and the rest of the values not initialized will be 0 by default.
But when I print all the values of the array letters, I am getting this output:
00000000000000000000000000000000000000000000000000000000000000000480030001000000000514303876720941309621-1392458268143038767232767-197939865932767-1979398659327670010143038792832767
I have no idea where the negative numbers are coming from.
'S' is the highest index you gave a value for and, as your encoding's apparently ASCII, the length of your array is 83 (= 0x53). Everything with smaller indexes (without an initializer) is initialized to 0.
When you print your array, you are accessing the array out-of-bounds, which is undefined behavior.
What your program probably outputs are the values which happen to be on the stack after your array. However, as said above, there is no guarantee about what will happen.
HTH
My guess is that the code you wrote to print the array is wrong. Just tested with this:
#include <stdio.h>
int main()
{
int letters[] = {['A'] = 4, ['B'] = 8, ['E'] = 3, ['I'] = 1, ['O'] = 0, ['S'] = 5};
int i;
for (i = 0; i <= 'S'; i++) {
printf("%d\n", letters[i]);
}
return 0;
}
And it works fine and prints everything up to and including 5.
You most probably are using a for loop that is structured as follows:
for ( int i = 0; i < sizeof letters; i++ ) {
// ...
}
And the problem lies within the sizeof letters part, which gives you the size of... letters, which is not how many elements the array has. It rather is, the size of letters, that is, size of a single element times amount of elements:
sizeof letters == sizeof * letters * amountofelements;
// evaluates to 1
// assuming that amountofelements is the amount of elements that
// the array letters points to
// more: sizeof * letters is equivalent to sizeof( letters[0] )
// and the equation above is equivalent to
// sizeof( letters ) == sizeof( letters[0] ) * amountofelements;
// just in case...
So, change your for condition into the following:
for ( int i = 0; i < sizeof letters / sizeof * letters; i++ ) {
// whatever
}
Thanks for giving me the opportunity to use my psychic powers, kek.

Selection Sort troubles with keeping pointers in correct spots post sort

I'm trying to execute a selection sort where i sort by the most goals scored. I have 3 categories; goals, assists, names. I can correctly sort by goals and keep the players goal's and assists in the correct spots after the sort, but when i try to move the names to the correct spot after the sort it only moves the first letter of the name. Here's my code. Thanks for the help!
void sortPlayersByGoals(int* goals, int* assists, char** names, int size)
{
int lh, rh, i, tempG, tempA, tempN;
for(lh = 0; lh < size; lh++)
{
rh = lh;
for(i = lh; i < size; i++)
{
if(goals[i] > goals[rh])
{
rh = i;
}
tempG = goals[lh];
tempA = assists[lh];
tempN = *names[lh];
goals[lh] = goals[rh];
*names[lh] = *names[rh];
assists[lh] = assists[rh];
goals[rh] = tempG;
*names[rh] = tempN;
assists[rh] = tempA;
}
}
}
Here's my output if that helps show my problem..
Pre-Sort
Name Goals Assists
Redden 2 0
Berglund 5 2
Jackman 2 0
Stewart 4 0
Oshie 3 5
McDonald 2 4
Pietrangelo 2 7
Perron 2 6
Tarasenko 5 5
Post-Sort
Name Goals Assists
Tedden 5 5
Berglund 5 2
Sackman 4 0
Otewart 3 5
Rshie 2 0
McDonald 2 4
Pietrangelo 2 7
Perron 2 6
Jarasenko 2 0
void sortPlayersByGoals(int* goals, int* assists, char** names, int size)
{ /* names is an array of pointers to char */
int lh, rh, i, tempG, tempA;
char *tempN; /* a pointer to one name */
for(lh = 0; lh < size; lh++)
{
rh = lh;
for(i = lh; i < size; i++)
{
if(goals[i] > goals[rh])
{
rh = i;
}
tempG = goals[lh];
tempA = assists[lh];
tempN = names[lh]; /* names[lh] is a pointer to the name in pos lh */
goals[lh] = goals[rh];
names[lh] = names[rh]; /* swap the pointers */
assists[lh] = assists[rh];
goals[rh] = tempG;
names[rh] = tempN; /* and not just the first letter */
assists[rh] = tempA;
}
}
}
Look at the relevant character copying code you have:
int tempN;
...
tempN = *names[lh];
*names[lh] = *names[rh];
*names[rh] = tempN;
Your "names" variable is a char** (I'll assume it's instantiated as an array of char* which you then pass as a pointer), so when you perform *names[lh], you're first indexing into your char** to get the char* at index lh, then dereferencing it. This is the same thing as indexing into the 0th element of the char* at index lh, which gives you its 1st character. Since you do this for all of your names-related operations, you're only ever just moving around the 1st character of your names. Also, your tempN variable is declared as an int, which is probably not what you intended.
You can fix it (with the smallest modification to your code) by looping over the length of the name you want to copy and assigning it character by character (instead of just the 1st character as you're doing now). Or you can use strcpy (or one of its variants, see http://msdn.microsoft.com/en-us/library/kk6xf663%28v=vs.110%29.aspx for a reference)
On a side note, if at all possible I would advocate for you just using strings instead. Also, you might want to think about having your data more tightly coupled, i.e. having a list of player structs that holds the player's {name, goals, and assists} and just re-arranging your list, but I guess that's a design decision that's up to you.

Resources