Свести (с номерами уровней) иерархический список
У меня есть коллекция экземпляров класса X, которые указывают на себя (на себя).
Например, мой класс может выглядеть так:
public class X {
string Name {get;set;}
List<X> Children {get;}
}
Теперь допустим, что у меня есть список экземпляров класса X, которые могут самостоятельно ссылаться на N уровней вниз.
Мой вопрос: как получить экземпляры X с N-го уровня в моем списке?
В основном я пытаюсь сделать в C# то, что рекурсивное самоссылающееся общее табличное выражение будет делать в SQL, то есть выровнять иерархический список, добавив номера уровней.
Я нашел этот пример: /questions/12843426/poisk-v-ierarhicheskom-spiske/12843447#12843447 Это прекрасно работает, но я все еще не могу понять, как получить элементы ТОЛЬКО с N-го уровня.
1 ответ
Ну, вы можете изменить Flatten
метод в связанном примере, чтобы включить номер уровня, а также по следующим направлениям:
public class Leveled<T>
{
public T Item {get; set;}
public int Level {get; set;}
}
public static IEnumerable<Leveled<T>> ToLeveled<T>(this IEnumerable<T> sequence,
int level)
{
return sequence.Select(item => new Leveled<T>{ Item = item, Level = level});
}
public static IEnumerable<Leveled<T>> FlattenLeveled<T>(this IEnumerable<T> sequence,
Func<T, IEnumerable<T>> childFetcher)
{
var itemsToYield = new Queue<Leveled<T>>(sequence.ToLeveled(0));
while (itemsToYield.Count > 0)
{
var leveledItem = itemsToYield.Dequeue();
yield return leveledItem;
var children = childFetcher(leveledItem.Item).ToLeveled(leveledItem.Level + 1);
if (children != null)
{
foreach (var child in children)
itemsToYield.Enqueue(child);
}
}
}
после этого вы можете просто отфильтровать необходимый уровень:
var thirdLevel = myCollection
.FlattenLeveled(item => item.Children)
.Where(leveledItem => leveledItem.Level == 2)
.Select(leveledItem => leveledItem.Item)
Кроме того, из комментария @Servy, поскольку это первый подход в ширину (все 1-го уровня выполняются до обработки любого 2-го уровня), мы можем использовать Skip/TakeWhile, например:
public static IEnumerable<T> GetHierarchyLevel<T>(this IEnumerable<T> sequence, Func<T, IEnumerable<T>> childFetcher, int level)
{
return sequence.FlattenLeveled(childFetcher)
.SkipWhile(li => li.Level < level)
.TakeWhile(li => li.Level == level)
.Select(li => li.Item);
}
Это будет лениво перечислять, поэтому любой уровень ниже по иерархии не будет обрабатываться вообще.