递归搜索分层列表

时间:2010-11-15 16:03:05

标签: c# linq lambda

我有一个分层的对象列表。假设结构如下:

  • 根节点
    • 父节点
      • 子节点
    • 父节点
      • 子节点
    • 父节点
      • 子节点

子节点可以有自己的子节点,但目标是基本搜索“父节点”。因此,假设父节点类具有名为“Name”的属性 - 并且用户输入部分名称,我希望返回其名称包含用户搜索条件的所有父节点。基本上,这更像是一个“过滤器”功能而不是任何东西。所以,我知道如何做到这一点,然而,我遇到的问题是他们的关键目标是保持层次结构的机智。换句话说,如果有一个父节点与筛选条件匹配,我希望返回以下结构:

  • 根节点
    • 父节点
      • 子节点

我目前的努力只会产生:

  • 父节点
    • 子节点

我正在使用Linq。任何建议都将不胜感激。

谢谢!

克里斯

以下针对当前过滤器实施的代码段:

FilteredReports = Reports.FirstOrDefault().Children.Cast<IHierarchicalResult>()
                                    .SelectRecursive(item => item.Children.Cast<IHierarchicalResult>())
                                    .Where(item => item.Name.ToLower().StartsWith(filterCriteria))
                                    .ToObservableCollection();

以下是我正在使用的扩展方法:

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;
                }
            }
        }
    }

3 个答案:

答案 0 :(得分:5)

由于您想要返回的Root节点与原始根节点(它的子节点较少)不同,因此您需要创建一个仅包含匹配子节点的新根节点。

这样的东西
  Node oldRootNode = ...
  List<Node> filteredChildren = oldRootNode.Children.Where(...).ToList();
  Node newRootNode = new Node {Name = oldRootNode.Name, Children = filteredChildren};

  return newRootNode;

答案 1 :(得分:0)

从内存(可能包含拼写错误)并基于不知道您的代码:

var filteredList = myRootNode.CollectionOfParentNodes.Where(p => p.Name.Contains(searchCriteriaString)).ToList();

答案 2 :(得分:0)

你可以在这做一些事情。

  1. 您可以创建主结构的副本并从过滤器返回(尽可能浅拷贝,但您必须深度复制节点之间的链接)
  2. 您可以扩展节点以了解它们是否已被过滤(即IsFilteredOut,ChildrenUnfilteredGet()等),然后显示“已过滤”的树。
  3. 您可以存储已过滤节点的列表(黑名单或白名单),然后在显示树时参考(这涉及最少的代码更改但处理能力最强)。