【问题标题】:Filter tree list retaining parent using LINQ使用 LINQ 过滤树列表保留父级
【发布时间】:2020-06-01 02:38:23
【问题描述】:

我有一个看起来像这样的模型类:

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 来执行此操作?

为了更清楚,如果过滤器搜索条件是 ID 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>() }
            }}
        }}
    }}
};

如果要过滤的 Id 是 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> () }
            } 
        }
    }
};

如果要过滤的 Id 是 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#

【问题讨论】:

  • 我不相信有任何扩展。您可以使用递归搜索。请分享您尝试过的代码
  • @PavelAnikhouski 我所做的是展平树列表并过滤结果列表并重新构建树。但不要认为这是最有效的解决方案,尽管我能够得到过滤后的数据
  • 请分享您当前的解决方案并编译您的代码示例,因为new List&lt;Employee&gt; employees 是无效声明

标签: c# linq


【解决方案1】:

这是您需要的方法:

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

此方法的两个关键行是第一行SelectWhere

第一个Select 跟踪当前employee 并递归调用FilterEmployees 以获取任何匹配的Reportees

Where 决定我们是否要保留当前的employee - 标准是员工是否满足谓词或满足其任何Reportees

最后,它只是构建新的Employee 来返回。

对于FilterEmployees(employees, e =&gt; e.Id == 6 || e.Id == 7),我们得到:

对于FilterEmployees(employees, e =&gt; e.Id == 6),我们得到:

对于FilterEmployees(employees, e =&gt; e.Id == 2),我们得到:

【讨论】:

    【解决方案2】:

    我有两个解决方案:

    1)创建一个函数以从其 ID 访问员工对象

    2) 创建一个 childsID 属性,如:

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

    在两者之后,您可以使用 linq 轻松完成这项工作

    【讨论】:

    • 请展示如何使用真正的可编译 C# 代码“创建 childsID 属性”。还请展示“您可以如何使用 linq 轻松完成这项工作”。
    猜你喜欢
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 2011-07-02
    • 2016-12-09
    • 1970-01-01
    相关资源
    最近更新 更多