Как отфильтровать массив структур по определенному параметру? - PullRequest
0 голосов
/ 13 мая 2018

Мне нужно написать программу, которая читает данные студентов из файла и сохраняет их в массив структур. После этого мне нужно отфильтровать данные, предложив пользователю. Мой входной файл, как показано ниже:

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

Я объявляю свою структуру для хранения данных как

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

Мне нужно спросить пользователя, хочет ли он просмотреть данные по имени или по теме. Если он выберет имя, то ему нужно будет выбрать, чьи данные будут показаны. Например, если он выбирает имя Kristina, программа должна вывести

Kristina
Science 30
History 45

Мне удалось прочитать из файла и сохранить данные в массиве структур, но я застрял в написании функции для вывода отфильтрованных данных. Мой код для функции

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;
        }
    }
}

После запуска моего кода это все тот же массив структур, который был прочитан из файла ранее. Мне нужен отфильтрованный массив, чтобы отсортировать его позже в моей программе.

Мне нужна копия соответствующих элементов, потому что мне понадобится существующий массив и для другой цели.

Кто-нибудь может сказать мне, как вернуть отфильтрованный массив?

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Предполагая, что вам просто нужно знать, какие элементы исходного массива struct s совпадают, вы можете просто вернуть массив указателей, указывающих на совпадающие элементы. Последняя запись в этом массиве указателей будет содержать NULL для обозначения конца совпадений.

Таким образом, функция фильтра может выглядеть следующим образом:

#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;
}

Используйте это так:

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);
  }
}

Чтобы отсортировать результат, просто отсортируйте массив указателей, созданный функцией фильтра.

0 голосов
/ 13 мая 2018

Вам нужно решить несколько проблем.

Во-первых, ваша функция фильтрации собирается вернуть несколько значений - сам отфильтрованный массив и его размер (длина). Как подсказывает связанный вопрос, вам нужно использовать struct или возвращать значение (я) по указателю. Например, последний:

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

Во-вторых, сам код. Поскольку вы хотите скопировать данные для существующих студентов, вы должны выделить результирующий массив:

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

Затем выполните цикл над учениками:

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

Внутри цикла проверьте, совпадает ли имя. Если да, не возвращает , но копирует один элемент из массива students в массив filtered:

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

Обратите внимание, как я использовал постинкремент filtered_size, чтобы скопировать новый элемент в конец нового массива. Это отслеживает размер нового массива.

...