Отфильтровать список дерева с сохранением родителя с помощью LINQ - PullRequest
1 голос
/ 26 мая 2020

У меня есть класс модели, который выглядит примерно так:

public class Employee
{ 
    public int Id {get;set;}
    public int ParentId {get;set;}
    public string Name{get;set;}
    public string Designation {get;set;}
    public List<Employee> Reportees {get;set;}
}

, используя который я смоделировал список демонстрационного дерева с данными:

var employees = new List<Employee>
{
    new Employee
    {
        Id = 1,
        ParentId = 0,
        Name = "A",
        Designation = "CEO",
        Reportees = new List<Employee>
        {
            new Employee { Id = 2, ParentId = 1,Name = "B",Designation = "Manager",Reportees = new List<Employee>
            {
                new Employee { Id = 4, ParentId = 2, Name = "D", Designation = "Lead", Reportees = new List<Employee>
                {
                    new Employee { Id = 6, ParentId = 4, Name = "F", Designation = "Developer", Reportees = new List<Employee>() },
                    new Employee { Id = 7, ParentId = 4, Name = "G", Designation = "Developer", Reportees = new List<Employee>() }
                }},
                new Employee { Id = 3, ParentId = 1,Name = "C",Designation = "Manager",Reportees = new List<Employee>
                {
                    new Employee { Id = 5, ParentId = 3, Name = "E", Designation = "Lead", Reportees = new List<Employee>
                    {
                        new Employee { Id = 8, ParentId = 5, Name = "H", Designation = "Developer", Reportees = new List<Employee>() }
                    }}
                }}
            }}
        }
    }
};

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

Доступны ли какие-либо расширения или можно использовать LINQ для этого?

Чтобы проясните, что это ожидаемый отфильтрованный список в случае, если критериями поиска фильтра являются идентификаторы 6 и 7 :

var employees = new List<Employee>
{
    new Employee {Id = 1, ParentId = 0, Name = "A",Designation = "CEO", Reportees = new List<Employee>
    {
        new Employee {Id = 2, ParentId = 1, Name = "B",Designation = "Manager", Reportees = new List<Employee>
        {
            new Employee {Id = 4, ParentId = 2, Name = "D", Designation = "Lead", Reportees = new List<Employee>
            {
                new Employee {Id = 6, ParentId = 4, Name = "F", Designation = "Developer", Reportees = new List<Employee>() },
                new Employee {Id = 7, ParentId = 4, Name = "G", Designation = "Developer", Reportees = new List<Employee>() }
            }}
        }}
    }}
};

и если идентификатор для фильтрации 6 :

new List<Employee> employees
{
    new Employee{Id = 1,ParentId = 0,Name = "A",Designation = "CEO",Reportees = new List<Employee>{
      new Employee{Id = 2,ParentId = 1,Name = "B",Designation = "Manager",Reportees = new List<Employee>{
        new Employee{Id = 4, ParentId = 2, Name = "D", Designation = "Lead", Reportees = new List<Employee>
            {
                new Employee{Id = 6, ParentId = 4, Name = "F", Designation = "Developer", Reportees = new List<Employee> () }
            } 
        }
    }
};

и если идентификатор для фильтрации равен 2 :

new List<Employee> employees
{
    new Employee{Id = 1,ParentId = 0,Name = "A",Designation = "CEO",Reportees = new List<Employee>{
      new Employee{Id = 2,ParentId = 1,Name = "B",Designation = "Manager",Reportees = new List<Employee>()}
    }
};C#

Ответы [ 2 ]

5 голосов
/ 26 мая 2020

Вот метод, который вам нужен:

List<Employee> FilterEmployees(List<Employee> source, Func<Employee, bool> predicate) =>
    source
        .Select(e => new { employee = e, reportees = FilterEmployees(e.Reportees, predicate) })
        .Where(x => predicate(x.employee) || x.reportees.Any())
        .Select(x => new Employee
        {
            Id = x.employee.Id,
            ParentId = x.employee.ParentId,
            Name = x.employee.Name,
            Designation = x.employee.Designation,
            Reportees = x.reportees,
        })
        .ToList();

Две ключевые строки этого метода - первая Select и Where.

Первая Select сохраняет отслеживает текущий employee и рекурсивно вызывает FilterEmployees, чтобы получить любое Reportees, которое соответствует.

Where решает, хотим ли мы сохранить текущее employee - критерии, которые выполнял сотрудник встретил предикат или выполнил какое-либо из его Reportees.

Наконец, он просто строит новый Employee для возврата.

Для FilterEmployees(employees, e => e.Id == 6 || e.Id == 7) мы получаем:

6 & 7

Для FilterEmployees(employees, e => e.Id == 6) получаем:

6

Для FilterEmployees(employees, e => e.Id == 2) получаем:

2

0 голосов
/ 26 мая 2020

У меня есть два решения для вас:

1) Создайте функцию для доступа к объекту сотрудника по его идентификатору

2) создайте свойство childsID, например:

new Employee{Id = 1,ParentId = 0, childsID=new list<int>(){2,4,6,7}, ...}

после обоих вы можете легко выполнить работу с linq

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