0

I am sorry if this sounds confusing, I will try to be as clear as possible. I have an array of structs, where the array stores a struct that I have defined as a Business Card. However, before adding any new business cards into the array, I have to store the structs in ascending order based on the integer value of the Employee ID.

Here is the struct:

typedef struct{
int nameCardID;
char personName[20];
char companyName[20];
} NameCard;

Hence, I tried to use relational operators to compare between the values of the ID and copy it in ascending order to another temporary array I named fakeHolder, before finally copying over to the actual array. However, I can't seem to understand why it is not in order after inputting my data as ID 9, 7, 5.

Here is my helper function:

int addNameCard(NameCard *nc, int *size){
    int i = 0;
    // Why is this a pointer?
    NameCard fakeHolder[10];
    char dummy[100];
    char *p;
    printf("addNameCard():\n");
    if(*size == MAX){
        printf("The name card holder is full");
        // To quit the program
        return 0;
    }
    // Keeps it to Fake Name Card Holder First
    printf("Enter nameCardID:\n");
    scanf("%d", &fakeHolder->nameCardID);
    scanf("%c", &dummy);
    printf("Enter personName:\n");
    fgets(fakeHolder->personName, 20, stdin);
    if(p = strchr(fakeHolder->personName, '\n')){
        *p = '\0';
    }
    printf("Enter companyName:\n");
    fgets(fakeHolder->companyName, 20, stdin);
    if(p = strchr(fakeHolder->companyName, '\n')){
        *p = '\0';
    }
    // Compare the ID value
    for(int j = 0; j < *size; j += 1){
        if(fakeHolder->nameCardID == (nc+j)->nameCardID){
            printf("The nameCardID has already existed");
        }
        else if(fakeHolder->nameCardID < (nc+j)->nameCardID){
            fakeHolder[(j+1)].nameCardID = (nc+j)->nameCardID;
            strcpy(fakeHolder[(j+1)].personName,(nc+j)->personName);
            strcpy(fakeHolder[(j+1)].companyName, (nc+j)->companyName);
        }
    }
    *size += 1;
    // Transfer to the Actual Name Card Holder
    for(int k = 0; k < *size; k += 1){
        (nc+k)->nameCardID = fakeHolder[k].nameCardID;
        strcpy((nc+k)->personName, fakeHolder[k].personName);
        strcpy((nc+k)->companyName, fakeHolder[k].companyName);
    }
    printf("The name card has been added successfully\n");
    return 0;
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • Just call `sort`. That's what it was made to do. You just need to write a simple little element comparison function that you pass as an argument. – Tom Karzes Nov 04 '21 at 08:36
  • @TomKarzes I'm sorry if I got anything wrong but when you say sort, are you suggesting that I should write another helper function that uses bubble sort to sort it? Thank you – Michelin_Boi Nov 04 '21 at 08:39
  • 1
    You need to provide a tiny callback function that decides whether 1 element of your array compare equal, less or greater than 1 other element. Then use [`qsort()`](https://man7.org/linux/man-pages/man3/qsort.3p.html) to do the sorting stuff. There is no need to re-invent the wheel unless you have specific requirements which are not met with standard library functions. – Gerhardh Nov 04 '21 at 08:58
  • Note that, if the array is already sorted, you can use binary search (also found in the standard library - [bsearch()](https://man7.org/linux/man-pages/man3/bsearch.3p.html)) to find *where* to insert a new element, and that this will be faster than fully sorting the array each time. – tucuxi Nov 04 '21 at 09:18
  • @Michelin_Boi Sorry it's `qsort`. It's a library function that sorts in O(n\*log(n)) expected time. Don't use bubble sort or the like. They're much slower. Plus `qsort` is already written for you. – Tom Karzes Nov 04 '21 at 10:59
  • @TomKarzes are you sure about the time? IIRC despite the name, `qsort` does not have to implement quicksort but could also do some bubble sort. – Gerhardh Nov 04 '21 at 11:29
  • @Gerhardh No one would ever implement bubble sort for `qsort`. `qsort` should be fast. – Tom Karzes Nov 04 '21 at 11:38
  • @Gerhardh thank you very much!! I managed to get it to work :> but for some reason my first input is not read but a zero appears in the ID. Does it have to do with the pointer notation in structs? – Michelin_Boi Nov 04 '21 at 15:55
  • I have no idea if you have some issue with accessing elements in your struct. We don't see your updated code. If you have tried to use `qsort` with callback function, it would be better to post a new question as that code will be quite different from this one. But some general hint: If you sort all but one entry correct, the access to your structs in your callback function is probably not that wrong. You probably just use `qsort` incorrectly. Or your input function is broken to begin with. – Gerhardh Nov 04 '21 at 16:09
  • @Gerhardh thank you for your hints and suggestions!! I managed to get it to work, but don't quite understand why, so I'll probably make a new post later!! thank you so much for your help!! – Michelin_Boi Nov 05 '21 at 05:01

1 Answers1

0

Your current code has several problems, and you can rewrite it to be much more maintainable and easier to work with. For example,

  • i (in int i = 0;) is not being used
  • scanf("%c", &dummy); is there, I assume, to remove trailing \n - but a 100-char buffer for a single character to read is... surprising. See scanf() leaves the new line char in the buffer for lots of discussion on different approaches to "trailing stuff after integer".
  • splitting addNameCard into 2 functions, one to actually request a NameCard and another to insert it into the array, would divide up responsibilities better, and make your program easier to test. Avoid mixing input/output with program logic.

The question you ask can be solved via the standard library qsort function, as follows:

#include <stdlib.h>

typedef struct{
    int nameCardID;
    char personName[20];
    char companyName[20];
} NameCard;

void show(NameCard *nc, int n) {    
    for (int i=0; i<n; i++, nc++) {
        printf("%d,%s,%s\n", 
            nc->nameCardID, nc->personName, nc->companyName);
    }
}

// comparison functions to qsort must return int and receive 2 const void * pointers
// they must then return 0 for equal, or <0 / >0 for lower/greater
int compareCardsById(const void *a, const void *b) {
    return ((NameCard *)a)->nameCardID - ((NameCard *)b)->nameCardID;
}

int main() {
    NameCard nc[10];
    nc[0] = (NameCard){1, "bill", "foo"};
    nc[1] = (NameCard){3, "joe", "bar"};
    nc[2] = (NameCard){2, "ben", "qux"};
    show(nc, 3);
    // calling the libraries' sort on the array; see "man qsort" for details
    qsort(nc, 3, sizeof(NameCard), compareCardsById);
    show(nc, 3);
    return 0;
}
tucuxi
  • 17,561
  • 2
  • 43
  • 74
  • Yes, the dummy array is to remove the trailing newline character. However, I had thought it to be necessary to have the size of the array to be on the larger side of things because I was thinking if the user tries to input multiple name cards, there would be multiple newline characters to be removed, which was why I made the array larger. Was I wrong in my thinking? – Michelin_Boi Nov 04 '21 at 09:25
  • I see!! Thank you for your advice!! Unfortunately, I do not think I can separate addNewCard() into 2 separate functions as my school assignment had indicated for this function to be able to do all of these. Ill read up about qsort!! thank you so much for your help – Michelin_Boi Nov 04 '21 at 09:26
  • The big recommendation regarding input is to use `fgets` + `sscanf`, instead of `fscanf` or `fgets` + `strchr`. Read whole lines (up to a max size) into a buffer, then use `sscanf` to pick out parts. This completely does away with unparsed-leftovers problems with `%d`, and using, say, `%9s` will guarantee a 9-char-tops 0-terminated string, without need of `strchr` to avoid newlines. – tucuxi Nov 04 '21 at 09:36
  • Ohhh I see!! Thank you :> I managed to get it to work :> but when I read in 3 sets of input, the first set of input will never be read in. So qsort() only sorts the other 2 inputs. I am thinking it might be because of nc->nameCard because the pointer will move to the next space automatically right? – Michelin_Boi Nov 04 '21 at 15:57