3

I need to write a program that reads students' details from a file and stores them into an array of structures. After that, I need to filter the data by prompting user. My input file is as below:

Kristina
Science 30
Desmond
Geography 78
Fred
Science 87
Kristina 
History 45
Desmond 
Mathematics 34

I declare my struct to store the data as

typedef struct {
    char name[102];
    char subject[40];
    int marks;
}Student;

I need to ask the user whether he wants to see the data by name or by subject. If he chooses name, then he will need to select whose data to be shown. For example, if he chooses the name Kristina, the program should output

Kristina
Science 30
History 45

I managed to read from file and store the data into array of structs, but I am stuck in writing the function to output the filtered data. My code for the function is

Student* filter_name(char choice[], Student* res, int index)
{
    int i;
    choice[strcspn(choice,"\n")]=0;
    for(i=0;i<index;i++)
    {
        if (strcmp(choice,res[i].name)==0)
        {
            return res;
        }
    }
}

After I run my code, it is still the same array of structs that was read from the file previously. I need the filtered array to sort it later in my program.

I need a copy of the elements that match, because i will need the existing array for another purpose as well.

Anyone can tell me how to return the filtered array?

alk
  • 69,737
  • 10
  • 105
  • 255
sterstar
  • 79
  • 7

2 Answers2

1

You have multiple problems to solve.

First, your filtering function is going to return multiple values - the filtered array itself and its size (length). As the linked question suggests, you need to use a struct or return the value(s) by pointer. For example of the latter:

Student* filter_by_name(char* name,Student* students,int students_size, int* filtered_size)
{
    ...
}

Second, the code itself. Since you want to copy the data for your existing students, you should allocate the resulting array:

Student* filtered = malloc(students_size * sizeof(Student));

Then do a loop over the students:

for (int i = 0; i < students_size; ++i)
{
    ...
}

Inside the loop, check whether the name matches. If yes, don't return but copy one element from the array students to the array filtered:

filtered[(*filtered_size)++] = students[i];

Note how I used post-increment of filtered_size to copy the new element to the tail of the new array. This keeps track of the size of the new array.

anatolyg
  • 26,506
  • 9
  • 60
  • 134
0

Assuming you just need to know which elements of the original array of structs match you then could just return an array of pointer pointing to the matching elements. The last entry in this pointer array would hold a NULL to indicate the end of matches.

So the filter function might look like this:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


Student const ** result_resize(Student const ** result, size_t size)
{
  void * pv = realloc((void*) result, size * sizeof *result);
  if (NULL == pv)
  {
    perror("realloc() failed");
    exit(EXIT_FAILURE);
  }

  return pv;
}

Student const ** students_filter_by_name(const char * name, Student * pstudent, size_t size)
{
  size_t matches = 0;
  size_t size_result = 1;
  Student const ** result = result_resize(NULL, size_result);

  for (size_t i = 0; i < size; ++i)
  {
    if (!strcmp(name, pstudent[i].name))
    {
      if (size_result <= matches)
      {
        size_result *= 2;

        result = result_resize(result, size_result);
      }

      result[matches] = &pstudent[i];
      ++matches;
    }
  }

  if (size_result <= matches)
  {
    ++size_result;
    result = result_resize(result, size_result);
  }

  result[matches] = NULL; /* Mark end-of-array */

  return result;
}

Use it like this:

Student const ** students_filter_by_name(
  const char * name, Student * pstudent, size_t size);

#define STUDENTS_MAX (42)

int main(void)
{
  Student students[STUDENTS_MAX] = {
    {"jack", "foo", 1},
    {"jill", "bar", 1},
    {"alk", "foo", 1},
    {"jill", "bar", 1},
    {"jack", "foo", 1},
    {"alk", "bar", 1},
  };

  /* Load data to array students here. */

  {
    const char * name = "alk";

    Student const ** ppmatches = students_filter_by_name(
      name, students, sizeof students / sizeof *students);

    {
      Student const ** pploop = ppmatches;

      printf("%s's marks:\n", name);

      while (pploop && *pploop)
      {
        printf("subject = '%s', mark = %d\n", (*pploop)->subject, (*pploop)->mark);
        ++pploop;
      }
    }

    free(ppmatches);
  }
}

To sort the result just sort the array of pointer created by the filter function.

alk
  • 69,737
  • 10
  • 105
  • 255