I have a hierarchical list of objects. Assume that the structure is as follows:
- Root node
- Parent node
- child node
- Parent node
- child node
- Parent node
- child node
- Parent node
The child nodes could have their own children, but the objective is to basically search the "parent nodes". So, let's say that the parent node class has a property called "Name" - and the user enters a partial name, I want all of the parent nodes whose name contains the user's search criteria to be returned. Basically, this is more of a "filter" functionality than anything. So, I know how to do this, however, the problem that I am running into is that they key objective is to keep the hierarchical structure in tact. In other words, if there is one parent node that matches the filter criteria, I want the structure below to be returned:
- Root node
- Parent node
- child node
- Parent node
My current efforts only yield:
- Parent node
- child node
I am using Linq. Any suggestions would be greatly appreciated.
Thanks!
Chris
Code snippet below for current filter implementation:
FilteredReports = Reports.FirstOrDefault().Children.Cast<IHierarchicalResult>()
.SelectRecursive(item => item.Children.Cast<IHierarchicalResult>())
.Where(item => item.Name.ToLower().StartsWith(filterCriteria))
.ToObservableCollection();
Here is the extension method I am using:
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren)
{
if (null == source)
{
throw new ArgumentNullException("source");
}
if (null == getChildren) return source;
return SelectRecursiveIterator(source, getChildren);
}
private static IEnumerable<T> SelectRecursiveIterator<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren)
{
foreach (T item in source)
{
yield return item;
IEnumerable<T> children开发者_如何转开发 = getChildren(item);
if (null != children)
{
foreach (T child in SelectRecursiveIterator(children, getChildren))
{
yield return child;
}
}
}
}
Since the Root node you want returned is NOT the same as the original root node (it has less children) you'll need to create a new root node containing just the children that match.
Something like
Node oldRootNode = ...
List<Node> filteredChildren = oldRootNode.Children.Where(...).ToList();
Node newRootNode = new Node {Name = oldRootNode.Name, Children = filteredChildren};
return newRootNode;
There are a few things you can do here.
- You can create a copy of the main structure and return it from your filter (shallow copying as much as possible, but you'll have to deep copy the links between nodes)
- You can extend your nodes to understand whether or not they've been filtered (i.e. IsFilteredOut, ChildrenUnfilteredGet(), etc.) and then display the "filtered" tree.
- You can store a list of filtered nodes (blacklist or whitelist) and then refer to that when displaying the tree (this involves the fewest code changes but the most processing power).
From memory (may contain typos) and based on not knowing your code:
var filteredList = myRootNode.CollectionOfParentNodes.Where(p => p.Name.Contains(searchCriteriaString)).ToList();
精彩评论