Solving demanding structure task

Viewed 170

The structure is given:

struct Date {
  int day, month, year;
};
struct Subject {
  int id;
  char name_of_subject[100];
  double ects;
};
struct Grade {
  char namesurname[100];
  int grade_value;
  struct Subject subject;
  struct Date date;
};

It is necessary to write a function digits_in_date that receives struct array of grades. For all students who have a total of 30 or more ECTS credits in the subjects they have passed (they have a grade of 6 or higher), it is necessary to exclude from the array (without changing the order!) those grades whose date contains the minimum number of different digits. Eg the date 20 February 2020 contains only two different digits (2 and 0). If they have more than one grade whose dates have the same (minimum) number of digits, all such grades should be dropped from the array. You can find out that the grades belong to the same student based on the name and surname (assume that if two students have the same name and surname, then it is the same student). Assume that the data is valid (valid dates, valid grades, ects credits are not negative, etc.) Auxiliary arrays are not allowed. Only allowed auxiliary array is when making histogram.

EXAMPLE:

struct Grade arr[3] = {
      {"Luc Manning", 10, {1, "Physics", 25}, {19, 1, 2020}},
      {"Kaia Feeney", 8, {1, "Math", 25}, {19, 1, 2020}},
      {"Luc Manning", 7, {2, "Linear Algebra", 10}, {11, 1, 2020}}};

EXPLANATION:

Sum of Luc Manning ECTS points is 35, so we check how many different digits are there in his subjects dates. Physics has 4, and Linear Algebra has 3 different digits in dates, so Linear Algebra should be removed. Sum of Kaia Feeney ECTS points is 25, so we don't check anything.

OUTPUT would be:

Luc Manning - Physics
Kaia Feeney - Math

INSTRUCTION: Create an auxiliary function that determines the number of different digits in the date using a histogram. Then the auxiliary function that calculates the number of ECTS credits and finally the ejection function. This will make your task drastically easier.

The only problem I have in code is that my program removes wrong subjects, and algorithms for the rest of task are correct. Half of the code are just autotests :) The code is not so long.

EDIT: after @pm100 made a function that would save 400 lines of code, and with my modification of original code, now the code is:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>

struct Date {
  int day, month, year;
};
struct Subject {
  int id;
  char name_of_subject[100];
  double ects;
};
struct Grade {
  char namesurname[100];
  int grade_value;
  struct Subject subject;
  struct Date date;
};

int num_digit(int n) {
  int num = 0;
  while (n != 0) {
    num++;
    n /= 10;
  }
  return num;
}

int check_digits(int d, int m, int y) {
  char dateStr[10];
  sprintf(dateStr, "%02d%02d%04d", d, m, y);
  int count[10] = {0};
  for (int i = 0; i < strlen(dateStr); i++) {
    char ch = dateStr[i];
    count[ch - '0']++;
  }
  int num = 0;
  for (int i = 0; i < 10; i++) {
    if (count[i] > 0)
      num++;
  }
  return num;
}

double credits(struct Grade arr[], int n, int i) {
  int j;
  double sum = arr[i].subject.ects;
  for (j = i + 1; j < n; j++)
    if (strcmp(arr[j].namesurname, arr[i].namesurname) == 0)
      sum += arr[j].subject.ects;
  return sum;
}

int find_min(struct Grade arr[], int n, int i) {
  int j,
      min = check_digits(arr[i].date.day, arr[i].date.month, arr[i].date.year);
  for (j = i + 1; j < n; j++)
    if (strcmp(arr[j].namesurname, arr[i].namesurname) == 0)
      if (check_digits(arr[j].date.day, arr[j].date.month, arr[j].date.year) <
          min)
        min = j;
  return min;
}

int digits_in_date(struct Grade arr[], int n) {
  int i, j, k, min;
  double sum_ects;
  for (i = 0; i < n; i++) {
    sum_ects = credits(arr, n, i);
    if (sum_ects >= 30) {
      min = find_min(arr, n, i);
      for (j = 0; j < n; j++)
        if (strcmp(arr[j].subject.name_of_subject,
                   arr[min].subject.name_of_subject) == 0) {
          for (k = j + 1; k < n; k++)
            arr[k - 1] = arr[k];
          j--;
          n--;
        }
    }
  }
  return n;
}

int main() {
  int i, s;
  printf("\nAT1: Trivial case\n");
  struct Grade arr1[3] = {
          {"Luc Manning", 10, {1, "Physics", 25}, {19, 1, 2020}},
          {"Kaia Feeney", 8, {1, "Math", 25}, {19, 1, 2020}},
          {"Luc Manning", 7, {2, "Linear Algebra", 10}, {11, 1, 2020}}};
  s = digits_in_date(arr1, 3);
  for (i = 0; i < s; i++)
    printf("%s - %s\n", arr1[i].namesurname, arr1[i].subject.name_of_subject);

  printf("\nAT2: We must not observe the same student more than once\n");
  struct Grade arr2[4] = {
      {"Mujo Suljic", 10, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},
      {"Fata Fatic", 8, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},
      {"Mujo Suljic", 7, {2, "Inzenjerska fizika", 10}, {11, 1, 2020}},
      {"Mujo Suljic", 10, {3, "Inzenjerska matematika", 6}, {1, 9, 2020}}};
  s = digits_in_date(arr2, 4);
  for (i = 0; i < s; i++)
    printf("%s - %s\n", arr2[i].namesurname, arr2[i].subject.name_of_subject);
    printf("CORRECT:\nMujo Suljic - Uvod u programiranje\nFata Fatic - Uvod u programiranje\nMujo Suljic - Inzenjerska matematika\n");

  printf("\nAT3: Multiple successive ejections\n");
  struct Grade arr3[6] = {
      {"Mujo Suljic", 10, {1, "Uvod u programiranje", 25}, {2, 2, 2020}},
      {"Mujo Suljic", 6, {5, "Osnove elektrotehnike", 25}, {20, 2, 2022}},
      {"Fata Fatic", 8, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},
      {"Mujo Suljic", 7, {2, "Inzenjerska fizika", 10}, {11, 1, 2020}},
      {"Mujo Suljic", 10, {3, "Inzenjerska matematika", 6}, {20, 1, 2020}},
      {"Fata Fatic", 8, {4, "Inzenjerska matematika", 25}, {9, 9, 2021}}};
  s = digits_in_date(arr3, 6);
  for (i = 0; i < s; i++)
    printf("%s - %s %g\n", arr3[i].namesurname, arr3[i].subject.name_of_subject,
           arr3[i].subject.ects);
    printf("CORRECT:\nMujo Suljic - Inzenjerska fizika 10\nMujo Suljic - Inzenjerska matematika 6\n");

  printf("\nAT4: Array with one element\n");
  struct Grade arr4[1] = {
      {"John Smith", 10, {1, "Subject 1", 29}, {19, 11, 1991}}};
  s = digits_in_date(arr4, 1);
  printf("%d ", s);
  arr4[0].subject.ects = 31;
  s = digits_in_date(arr4, 1);
  printf("%d\n", s);
 printf("CORRECT:\n1 0\n");

  printf(
      "\nAT5: We don't eject anything out, no one has 30 credits in total\n");
  struct Grade arr5[6] = {
      {"Mujo Suljic", 10, {1, "Uvod u programiranje", 25}, {20, 2, 2020}},
      {"Pero Peric", 6, {5, "Osnove elektrotehnike", 25}, {20, 2, 2020}},
      {"Fata Fatic", 8, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},
      {"Mujo Suljic", 7, {2, "Inzenjerska fizika", 1}, {11, 1, 2020}},
      {"Mujo Suljic", 10, {3, "Inzenjerska matematika", 2}, {20, 1, 2020}},
      {"Fata Fatic", 8, {4, "Inzenjerska matematika", 2}, {19, 1, 2020}}};
  s = digits_in_date(arr5, 6);
  printf("s: %d\n", s);
  printf("AT5 again: Inzenjerska fizika now carries 4 ECTS credits so Mujo has "
         "the required number of credits\n");
  arr5[3].subject.ects = 4;
  s = digits_in_date(arr5, 6);
  for (i = 0; i < s; i++)
    printf("%s - %s %g\n", arr5[i].namesurname, arr5[i].subject.name_of_subject,
           arr5[i].subject.ects);
           printf("CORRECT:\ns: 6\nAT5 again: Inzenjerska fizika now carries 4 ECTS credits so Mujo has "
         "the required number of credits\nPero Peric - Osnove elektrotehnike 25\nFata Fatic - Uvod u programiranje 25\nMujo Suljic - Inzenjerska fizika 4\n"
"Mujo Suljic - Inzenjerska matematika 2\nFata Fatic - Inzenjerska matematika 2\n");

  //
  printf("\nAT6: Mujo Suljic did not pass Injzenjerska fizika, he does not "
         "have 30 ECTS credits\n");
  struct Grade arr6[6] = {
      {"Mujo Suljic", 10, {1, "Uvod u programiranje", 25}, {20, 2, 2020}},
      {"Pero Peric", 6, {5, "Osnove elektrotehnike", 25}, {20, 2, 2020}},
      {"Fata Fatic", 8, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},
      {"Mujo Suljic", 5, {2, "Inzenjerska fizika", 10}, {11, 1, 2020}},
      {"Mujo Suljic", 10, {3, "Inzenjerska matematika", 4}, {20, 1, 2020}},
      {"Fata Fatic", 8, {4, "Inzenjerska matematika", 25}, {19, 1, 2020}}};
  s = digits_in_date(arr6, 6);
  for (i = 0; i < s; i++)
    printf("%s - %s %g\n", arr6[i].namesurname, arr6[i].subject.name_of_subject,
           arr6[i].subject.ects);
            printf("CORRECT:\nMujo Suljic - Uvod u programiranje 25\nPero Peric - Osnove elektrotehnike 25\n"
"Mujo Suljic - Inzenjerska fizika 10\nMujo Suljic - Inzenjerska matematika 4\n");
  return 0;
}

I only get correct output for AutoTest1 - AT1 (which is the same as example), and the rest AT are wrong. Could you help me modify this to work properly?

3 Answers

You had some very simple bugs in the end

I leave it to you to work out what was wrong (hint you were confused about what 'min' is, plus 2 other errros)

int find_min(struct Grade arr[], int n, int i) {
    int j,
        min = 10;// check_digits(arr[i].date.day, arr[i].date.month, arr[i].date.year);
    int minIdx = -1;
    for (j = i ; j < n; j++)
        if (strcmp(arr[j].namesurname, arr[i].namesurname) == 0)
            if (check_digits(arr[j].date.day, arr[j].date.month, arr[j].date.year) <
                min)
                minIdx = j;
    return minIdx;
}

Here is my count digits function

convert to a string and then create a distribution 'histogram' in count[]

Then count the number of non 0 counts

int count_digits(int d, int m, int y)
{
    char dateStr[10];
    sprintf(dateStr,"%02d%02d%04d", d, m, y);
    int count[10] = { 0 };
    for (int i = 0; i < strlen(dateStr); i++) {
        char ch = dateStr[i];
        count[ch - '0']++;
    }
    int tally = 0;
    for (int i = 0; i < 10; i++)
    {
        if (count[i] > 0)
            tally++;
    }
    return tally;
}

saving about 380 lines

I think you misundertand the instructions. You think you have to reject all but the bigsest for a student, no you must reject the minimums.

For at2

    {"Mujo Suljic", 10, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},  <<==4
    {"Fata Fatic", 8, {1, "Uvod u programiranje", 25}, {19, 1, 2020}},
    {"Mujo Suljic", 7, {2, "Inzenjerska fizika", 10}, {11, 1, 2020}}, <<==3
    {"Mujo Suljic", 10, {3, "Inzenjerska matematika", 6}, {1, 9, 2020}} }; <<< = 4

you must elimiate the 3 line

 {"Mujo Suljic", 7, {2, "Inzenjerska fizika", 10}, {11, 1, 2020}},

but in your 'comment' you say

printf("\nAT2: We must not observe the same student more than once\n");

the answer shows that you must show the same student more than once.

I think you can fix the code, now that you see you have misunderstood the logic of the assignment


You also make the mistake of not starting at the beginning of the array in some places

double credits(struct Grade arr[], int n, int i) {
    int j;
    double sum = arr[i].subject.ects;
    for (j = i + 1; j < n; j++)
        if (strcmp(arr[j].namesurname, arr[i].namesurname) == 0)
            sum += arr[j].subject.ects;
    return sum;
}

I added

double credits(struct Grade arr[], int n, int i) {
    int j;
    double sum = arr[i].subject.ects;
    for (j = i + 1; j < n; j++)
        if (strcmp(arr[j].namesurname, arr[i].namesurname) == 0)
            sum += arr[j].subject.ects;
    printf("credits = %f for %s\n", sum, arr[i].namesurname); <<<<==
    return sum;
}

for AT3 I get

AT3: Multiple successive ejections
credits = 66.000000 for Mujo Suljic
credits = 25.000000 for Fata Fatic

which is not correct

fata has 50.

Its becuase you start half way though. I think you make the same mistake with min calcuation

Related