Как сгруппировать список по списку и объединить его в один список без повторений в C#? - PullRequest
2 голосов
/ 26 апреля 2020

У меня есть класс с именем studentdetails и свойство студента, которое является списком студентов.

public class studentdetails 
{
    public int SubjectId  {get ; set; }
    public int studentId  { get; set; } 
    public int ClassId  { get; set; }     
}

List<studentdetails> students = new List<studentdetails>()
{ 
    new studentdetails() { studentId = 1, SubjectId = 1, ClassId = 1 },
    new studentdetails() { studentId = 2, SubjectId = 2, ClassId = 1 },
    new studentdetails() { studentId = 3, SubjectId = 1, ClassId = 2 },
    new studentdetails() { studentId = 1, SubjectId = 3, ClassId = 2 },
    new studentdetails() { studentId = 1, SubjectId = 3, ClassId = 1 }
};

Мне нужно создать список класса, который содержит список студентов (который содержит список объектов sbject). как свойство) как свойство без повторения класса, ученика и предмета, если он уже существует.

пример:

public class Class
{
    public int ClassId  { get; set; }
    public Lits<student> students  { get; set; }
}
public class student
{
    public int StudentId  { get; set; }
    public Lits<Subject> subjects  { get; set; }
}
public class Subject 
{
    public int SubjectId   { get; set; }
}

Например: с указанием выше студенческие детали

class(1) -student(1) -subject(1)
                     -subject(3)
         -student(2) -subject(2)

class(2) -student(3) -subject(1)
         -student(1) -subject(3)

1 Ответ

2 голосов
/ 27 апреля 2020

Для решения проблемы можно использовать следующий подход:

List<Class> classes = students
    // This GroupBy creates groups by ClassId:
    // (ClassId) -> (List of Students).
    .GroupBy(s => s.ClassId)
    .Select(c => new Class
    {
       ClassId = c.Key,
       Students = c
          // This GroupBy for given Class creates groups by StudentId:
          // (StudentId) -> (List of Subjects).
          .GroupBy(s => s.StudentId)
          .Select(s => new Student
          {
             StudentId = s.Key,
             Subjects = s
                // This GroupBy for given Class and Student removes
                // duplicate values of SubjectId. If you can guarantee
                // that for given Class and Student will not be duplicate
                // values of SubjectId then you can remove this GroupBy.
                // If you remove this GroupBy then you need to change 
                // expression inside Select to the following:
                // new Subject { SubjectId = t.SubjectId }.
                .GroupBy(t => t.SubjectId)
                .Select(t => new Subject { SubjectId = t.Key })
                .ToList()
          }).ToList()
    }).ToList();

Вот полный образец , который демонстрирует этот подход.


@ Такс спросил в комментарии:

Если есть имя класса, имя студента и название деятельности наряду с идентификаторами. Тогда как я могу сопоставить соответственно экземпляру?

Если вам необходимо дополнительно сопоставить свойства, отличные от id, тогда вам следует использовать следующую перегрузку GroupBy метода: GroupBy(keySelector, comparer) . Используя этот метод, мы можем использовать экземпляры класса StudentDetails в качестве ключей и указать для них comparer.

Сначала мы должны создать comparer, класс, реализующий интерфейс IEqualityComparer. В нашем примере мы можем использовать один класс comparer для выполнения всех трех операций GroupBy, поскольку все наши операции GroupBy выполняются с использованием свойства int Id. Обычно каждая операция GroupBy использует свой собственный класс comparer, потому что большую часть времени различные операции GroupBy выполняются с использованием разных ключей (разные типы данных, разное количество группирующих свойств). Вот как мы можем реализовать наши comparer:

// For demo I simplified implementation of the Equals and GetHashCode
// methods by excluding null checks. In the documentation of 
// IEqualityComparer you can find implementation with null checks.
public class ComparerById<T> : IEqualityComparer<T>
{
    private readonly Func<T, int> _keySelector;

    public ComparerById(Func<T, int> keySelector) => _keySelector = keySelector;

    public bool Equals(T x, T y) => _keySelector(x) == _keySelector(y);

    public int GetHashCode(T obj) => _keySelector(obj);
}

И затем с помощью этого comparer мы можем выполнить необходимые GroupBy:

List<Class> classes = students
    // Now key of each group has type StudentDetails, therefore later we
    // will be able to use properties of StudentDetails such as ClassName.
    // Here to compare keys of type StudentDetails we use comparer:
    // new ComparerById<StudentDetails>(s => s.ClassId);
    // It means that we create groups by ClassId.
    .GroupBy(s => s, new ComparerById<StudentDetails>(s => s.ClassId))
    .Select(c => new Class
    {
        ClassId = c.Key.ClassId,
        ClassName = c.Key.ClassName,
        Students = c
            // Here we create groups by StudentId.
            .GroupBy(s => s, new ComparerById<StudentDetails>(s => s.StudentId))
            .Select(s => new Student
            {
                StudentId = s.Key.StudentId,
                StudentName = s.Key.StudentName,
                Subjects = s
                    // Here we create groups by SubjectId.
                    .GroupBy(t => t, new ComparerById<StudentDetails>(t => t.SubjectId))
                    .Select(t => new Subject {SubjectId = t.Key.SubjectId, SubjectName = t.Key.SubjectName})
                    .ToList()
            }).ToList()
    }).ToList();

Здесь завершено образец , который показывает этот подход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...