简体   繁体   中英

C# LINQ select hierarchy from list where root has a certain property value

I have a class with the following properties:

  • int ID
  • int? ParentID
  • bool? NeedsShowing

I have a list of these objects, which list represents multiple hierarchies: ID is the key value while ParentID is acting as a foreign key. NeedsShowing is only filled at the root level of each hierarchy, meaning it is only present when ParentID == null .

What I need to accomplish - preferably with the simplest possible LINQ - is to select only those hierarchies in the list, where NeedsShowing == true (basically applying a filter).

I have approximately 50,000 objects in the list representing roughly 13,000 hierarchies.

Code

Public Class FileHierarchy
{
  int ID { get; set; }
  int? ParentID { get; set; }
  bool? NeedsShowing { get; set; }
}

List of FileHierarchy class with values:

ID     ParentID       NeedsShowing
----------------------------------
1      null           true
2      1              null
3      1              null
4      2              null
5      null           false
6      5              null
7      6              null
8      null           true
9      8              null

This means I have three hierarchies with root node ID 1, 5, 8 (where parent ID == null ). I only want to get all nodes in hierarchies with root ID 1 and 8, because root ID 5 has NeedsShowing == false .

Try this

List<FileHierarchy> mylist = GetList();
var selected = mylist.Where(s => s.NeedsShowing.HasValue && s.NeedsShowing.Value);
var children = mylist.Where(c => c.ParentID.HasValue && selected.Select(s => s.ID).Contains(c.ParentID.Value));
var unselected = mylist.Except(selected);
while (children.Any())
{
    unselected = unselected.Except(children);
    var childChild = unselected.Where(c => c.ParentID.HasValue && children.Select(s => s.ID).Contains(c.ParentID.Value));
    selected = selected.Union(children);
    children = childChild;
}

I think this is a bit more readable and debuggable and you can tweak the result if you'd like. Maybe faster (haven't compared directly). There's really no way to do it other than recursively or ultra-iteratively like @Zrethreal's answer. I prefer recursion.

    private static void Main(string[] args)
    {
        var files = GetFiles();
        var searchableFiles = files.Except(files.Where(f1 => f1.NeedsShowing ?? false && f1.ParentID == null)).ToList()
        var whereNeedShowing = (from f in files
                                where f.NeedsShowing ?? false
                                select new {Root = f, Descendants = FindDescendants(f.ID, searchableFiles) }).ToList();
        Debug.Assert(whereNeedShowing.Count == 2);
    }

    static List<FileHierarchy> FindDescendants(int? parentId, IEnumerable<FileHierarchy> files)
    {
        var children = files.Where(f => f.ParentID == parentId).ToList();
        foreach(var child in children.ToList())
        {
            var newChildren = FindDescendants(child.ID, files.Except(children));
            children.AddRange(newChildren);
        }
        return children;
    }

    static List<FileHierarchy> GetFiles()
    {
        var result = new List<FileHierarchy>();
        for (int i = 1; i <= 9; i++)
        {
            var file = new FileHierarchy()
            {
                ID = i, 
                ParentID = i == 1 || i == 5 || i == 8 ? (int?) null : i - 1, 
                NeedsShowing = i == 1 || i == 8 ? true : (i == 5 ? false : (bool?) null)
            };
            result.Add(file);
            if (i == 3)
            {
                file.ParentID = 1;
            }
            else if (i == 4)
            {
                file.ParentID = 2;
            }
        }
        return result;
    }
}

public class FileHierarchy
{
    public int ID { get; set; }
    public int? ParentID { get; set; }
    public bool? NeedsShowing { get; set; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM