Reputation: 1469
I have a class with the following properties:
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.
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
.
Upvotes: 0
Views: 1882
Reputation: 1086
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; }
}
Upvotes: 0
Reputation: 338
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;
}
Upvotes: 1