使用多个根节点展平IEnumerable并选择Id属性

时间:2014-01-18 12:26:41

标签: c# linq c#-4.0

我有以下层次结构,我需要将其展平并选择所有Id。我尝试使用SelectMany()这样的.SelectMany(node => node.Children).Select(node => node.Id)。这将产生 3,5,6 的列表。是否有可能使用Linq获得完整列表 1,2,3,4,5,6,7

  • 节点(Id = 1)
  • 节点(Id = 2)
    • 节点(Id = 3)
  • 节点(Id = 4)
    • 节点(Id = 5)
    • 节点(Id = 6)
      • 节点(Id = 7)

3 个答案:

答案 0 :(得分:2)

您可以使用以下扩展方法来展平层次结构(请参阅下面的答案更新中的替代展平算法):

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector)
{
    foreach (var item in source)
    {
        yield return item;

        var children = childrenSelector(item);
        if (children == null)
            continue;

        foreach (var child in children.Flatten(childrenSelector))
            yield return child;                
    }
}    

我接受子选择器并递归生成子项。然后投影很简单:

var result = nodes.Flatten(n => n.Children).Select(n => n.Id);

假设您有以下Node类:

public class Node
{    
    public Node(int id, params Node[] children)
    {
        Id = id;
        if (children.Any())
            Children = new List<Node>(children);
    }

    public int Id { get; set; }
    public List<Node> Children { get; set; }
}

然后使用您的样本层次结构:

List<Node> nodes = new List<Node> {
    new Node(1),
    new Node(2, new Node(3)),
    new Node(4, new Node(5),
                new Node(6, new Node(7)))
};

输出将是:

1, 2, 3, 4, 5, 6, 7

UPDATE :您可以在不使用递归的情况下展平层次结构(以获得更好的性能):

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector)
{
    Queue<T> queue = new Queue<T>();
    foreach (var item in source)
        queue.Enqueue(item);

    while (queue.Any())
    {
        T item = queue.Dequeue();
        yield return item;
        var children = childrenSelector(item);
        if (children == null)
            continue;

        foreach (var child in children)
           queue.Enqueue(child);
    }
}

答案 1 :(得分:0)

易。

Func<IEnumerable<Node>, IEnumerable<int>> flatten = null;
flatten = ns =>
{
    return ns.Select(n => n.Id)
        .Concat(ns.SelectMany(n => flatten(n.Children)));
};

答案 2 :(得分:0)

您可以使用扩展方法展平层次结构:

public static IEnumerable<Node> Flatten(this IEnumerable<Node> source)
{
  if(source == null) 
    throw new ArgumentNullException("source");

  return FlattenIterator(source);
}

private static IEnumerable<Node> FlattenIterator(IEnumerable<Node> source)
{
  foreach(var node in source)
  {
    yield return node;
    foreach(var child in node.Children.Flatten())
    {
      yield return child;
    }
  }
}

然后你可以选择这样的ID:

var ids = source.Flatten().Select(n => n.Id);