2010-11-15 5 views
2

У меня есть иерархический список объектов. Предположим, что структура выглядит следующим образом:Поиск иерархического списка рекурсивно

  • корневого узла
    • родительского узла
      • дочернего узла
    • родительского узла
      • дочернего узла
    • родительский узел
      • дочерний узел

Дочерние узлы могут иметь своих собственных детей, но цель состоит в том, чтобы в основном искать «родительские узлы». Итак, предположим, что класс родительского узла имеет свойство «Имя» - и пользователь вводит частичное имя, я хочу, чтобы все родительские узлы, чье имя содержит критерии поиска пользователя, которые будут возвращены. В принципе, это больше функциональность «фильтра», чем что-либо. Итак, я знаю, как это сделать, однако проблема, с которой я сталкиваюсь, заключается в том, что главная цель - поддерживать иерархическую структуру в такте. Другими словами, если есть один родительский узел, который соответствует критериям фильтра, я хочу структуру ниже должны быть возвращены:

  • Корневой узел
    • Родительский узел
      • дочерний узел

Мои текущие усилия:

  • Родительский узел
    • дочерний узел

Я использую Linq. Любые предложения будут ценны.

Спасибо!

Chris

Фрагмент кода ниже текущей реализации фильтра:

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

Не могли бы вы предоставить нам более подробную информацию? Какой тип коллекции является вашим иерархическим списком? Являются ли дочерние узлы инкапсулированы в родительские узлы или связаны с ними в структуре типа дерева? Фрагменты кода хороши! –

+0

Коллекция представляет собой ObservableCollection. Каждый узел имеет свойство «Дети» - это коллекция IEnumerable того же класса. Поэтому, чтобы простое выполнение этого требования, используя метод (ы) расширения или какой-либо другой чистый подход, могу ли я фильтровать исходные дочерние узлы корневого узла и по-прежнему удерживать корневой узел в такте? – Chris

+0

Я думаю, что мне нужно будет иметь две коллекции. Одна коллекция - это оригинальная коллекция объектов, а вторая коллекция будет моей динамической коллекцией, которая представляет отфильтрованные результаты. Я также не хочу предполагать, что у меня всегда будет только один корневой узел. – Chris

ответ

5

Поскольку возвращаемый корневой узел не совпадает с исходным корневым узлом (у него меньше детей), вам нужно создать новый корневой узел, содержащий только дочерние элементы, которые соответствуют.

Что-то вроде

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

    return newRootNode; 
+0

Мне нравится это решение. Если вы не против копирования корневого узла и нужно только фильтровать в первой дочерней коллекции, я бы пошел с этим. – Kendrick

0

Из памяти (могут содержать опечатки) и на основании не зная ваш код:

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

Он хочет, чтобы дерево strucutre назад, а не список. – Kendrick

+0

Хорошо, я неправильно прочитал вопрос и ответил слишком быстро. Не содержит исходный корневой узел. Ответ Хайтечердера подходит мне. –

0

Есть несколько вещей, которые вы можете сделать здесь.

  1. Вы можете создать копию основной структуры и вернуть его из фильтра (неглубокое копирование как можно больше, но вы должны будете глубокой копии связей между узлами)
  2. Вы можете расширить свои узлы чтобы понять, были ли они отфильтрованы (т.е. IsFilteredOut, ChildrenUnfilteredGet() и т. д.), а затем отобразить «фильтрованное» дерево.
  3. Вы можете сохранить список фильтрованных узлов (черный список или белый список), а затем обратиться к этому при отображении дерева (это связано с наименьшим количеством изменений кода, но большей вычислительной мощностью).
Смежные вопросы