Для решения проблемы можно использовать следующий подход:
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();
Здесь завершено образец , который показывает этот подход.