Список в иерархию

#c# #linq #hierarchy #hierarchical-data

#c# #linq #иерархия #иерархический-данные

Вопрос:

У меня есть List<Data> где данные

 public class Data
{
  public string SchoolId {get; set;}
  public string SchoolName {get; set;}
  public string TeacherId {get; set;}
  public string TeacherName {get; set;}
}
  

Список является плоским:

 SchoolId    SchoolName     TeacherId      TeacherName
1           X              1              Mr X
1           X              2              Mrs Y
2           Y              3              Mr Z
2           Y              1              Mr X
  

Таким образом, в принципе, учитель может принадлежать ко многим школам.

Как я могу преобразовать приведенный выше плоский список в List<School>

 public class School
{
     public School()
     {
          this.Teachers  = new List<Teacher>();
     }
     public string SchoolId { get; set; }
     public string SchoolName { get; set; }
     public List<Teacher> Teachers {get; set;}
}  


public class Teacher
{
    public string TeacherId { get; set; }
    public string TeacherName { get; set; }
} 
  

Ответ №1:

Если это ссылка на объекты, то используйте этот код

 var result = list.GroupBy(x=>new {x.SchoolId, x.SchoolName})
.Select(x=>
{
    var s = new School();
    s.SchoolId = x.Key.SchoolId;
    s.SchoolName = x.Key.SchoolName;
    s.Teachers.AddRange(x.Select(
        y => new Teacher
        {
            TeacherId = y.TeacherId,
            TeacherName = y.TeacherName
        }
    ));

    return s;
});
  

Пожалуйста, обратите внимание, что приведенный выше код приведет к дублированию Teacher экземпляра даже среди Teacher одинаковых Id .

Комментарии:

1. s.Teachers может быть null

2. @dotctor Это не должно быть для нового объекта, потому что он инициализирован в School конструкторе.

3. Вы правы. Просто посмотрите на конструктор School .

Ответ №2:

Порядок этого подхода равен O (n ^ 2), и он не использует экземпляры учителей.

 var schools = data
    .GroupBy(x => x.SchoolId)
    .Select(group => new School()
    {
        SchoolId = group.Key,
        SchoolName = group.First().SchoolName,
        Teachers = data.Where(x => x.SchoolId == group.Key)
            .Select(x => new Teacher()
            {
                TeacherId = x.TeacherId,
                TeacherName = x.TeacherName
            })
            .ToList()
    })
    .ToList();
  

Если вы хотите поделиться экземплярами учителей, вы можете использовать это

 var teachersById = data
    .GroupBy(x => x.TeacherId)
    .Select(group => new Teacher()
    {
        TeacherId = group.Key,
        TeacherName = group.First().TeacherName
    })
    .ToDictionary(x => x.TeacherId);

var schools = data
    .GroupBy(x => x.SchoolId)
    .Select(group => new School()
    {
        SchoolId = group.Key,
        SchoolName = group.First().SchoolName,
        Teachers = teachersById 
            .Where(kv => data
                .Where(x => x.SchoolId == group.Key)
                .Select(x => x.TeacherId)
                .Contains(kv.Key)
            )
            .Select(x => x.Value)
            .ToList()
    })
    .ToList();
  

Комментарии:

1. Не должен ли второй быть простым Teachers = group.Select(x => teachersById[x.TeacherId]).ToList() (чтобы воспользоваться реальным преимуществом словаря)

Ответ №3:

 List<Data> dataList = new List<Data>();

IEnumerable<School> schools = from d in dataList
                              group d by new { SchoolId = d.SchoolId, SchoolName = d.SchoolName } into groupSchool
                              select new School { SchoolId = groupSchool.Key.SchoolId, SchoolName = groupSchool.Key.SchoolName, Teachers =new List<Teacher>(groupSchool.Select(x => new Teacher { TeacherId = x.TeacherId, TeacherName = x.TeacherName })) };
  

Ответ №4:

элемент.Школьное примечание: это псевдокод (для краткого кода я использовал конструкторы с параметрами вместо использования свойств)

 List<Data> data = //....;
Dictionary<string, Teacher> teachers = new Dictionary<string, Teacher>();
Dictionary<string, School> schools = new Dictionary<string, School>();
foreach (var item in data)
{
   if (item.TeacherId not in teachers)
      teachers.add(item.TeacherId, new Teacher(item.TeacherId, item.TeacherName));
}
foreach (var item in data)
{
   if (item.SchoolId not in schools)
      schools.add(item.SchoolId , item.SchoolName, new School(item.SchoolId , teachers[item.SchoolId]));
}
List<School> gen_schools =     // get values from schools;
  

P.S. На самом деле вы используете неправильное представление своей базы данных (вам следует разделить учителей из школ на две разные таблицы).