【问题标题】:LINQ: How to convert the nested hierarchical object to flatten objectLINQ:如何将嵌套的分层对象转换为展平对象
【发布时间】:2009-12-21 05:42:54
【问题描述】:

如何使用 LINQ 将嵌套的分层对象转换为扁平对象?我知道我们可以很容易地使用 foreach 循环来实现这一点。但我想知道是否有办法在 LINQ 中编写它。

class Person{
   public int ID {get;set}
   public string Name {get;set}
   public List<Person> Children {get;}
}

数据:

ID   : 1

Name : Jack

Children

2 | Rose 

3 | Paul

我喜欢将这些数据转换为扁平格式,如下所示。

1 | Jack 

2 | Rose 

3 | Paul

我们如何用 Linq 做到这一点?

【问题讨论】:

    标签: linq


    【解决方案1】:

    如果你想让它压扁一棵任意深的人树,我建议如下:

    public IEnumerable<Person> GetFamily(Person parent)
    {
        yield return parent;
        foreach (Person child in parent.Children) // check null if you must
            foreach (Person relative in GetFamily(child))
                yield return relative;
    }
    

    实际上没有什么好的方法可以用 LINQ 来缩短它,因为匿名 lambda 不能在不实现 Y 的情况下递归调用自己。您可以将上述方法“简化”为

    return parent.Children.SelectMany(p => GetFamily(p))
                          .Concat(new Person[] { parent });
    

    或者

    yield return parent;
        foreach (Person relative in parent.Children.SelectMany(GetFamily))
            yield return relative;
    

    但这对我来说似乎有点不必要。

    【讨论】:

    • 当然 lambdas 可以调用自己。这是使用递归 lambda 的斐波那契:Func&lt;int, int&gt; fib = null; fib = i =&gt; i &lt;= 1 ? i : fib(i-1) + fib(i-2);
    • 我说“anonymous lambdas 不能调用自己”,这就是为什么他不能编写返回他想要的值的单个表达式——他需要声明要递归的命名函数。
    • 那么通过将匿名的东西放入变量中,它就不再是匿名的了?例如。 var a = new { X = 5 };?我仍然将 a 引用的内容称为匿名类型。微软无条件地表示"A lambda expression is an anonymous function" 并查看Anonymous Methods 的第二个示例。几乎所有匿名的东西都必须放在某种命名变量或参数中;否则它们不能被代码使用。这并不意味着他们不是匿名的。
    • 我将继续使用“匿名”一词来表示“未按名称识别”。在您的示例中,类型是匿名的,但对象不是匿名的。
    • 您只是在以一种我认为不常见的方式使用单词。我呼吁维基百科页面:en.wikipedia.org/wiki/Anonymous_function “在编程语言理论中,匿名函数(也是函数常量、函数文字或 lambda 函数)是定义的函数(或子例程),并且可能被调用,而不是绑定到一个标识符。” 说“对象没有名字”并不是我喜欢使用“名字”这个词的方式,所以我们将对此保持分歧。
    【解决方案2】:

    这是一个不错的、通用且可重用的扩展方法:

    static public IEnumerable<T> Descendants<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> descendBy)
    {
        if (!source.IsNullOrEmpty())
        {
            foreach (T value in source)
            {
                yield return value;
    
                if (!descendBy(value).IsNullOrEmpty())
                {
                    foreach (T child in descendBy(value).Descendants<T>(descendBy))
                    {
                        yield return child;
                    }
                }
            }
        }
    }
    

    在上述情况下,像这样使用:

    var allChildren = parent.Children.Descendants(p => p.Children);
    

    一个小问题是它不包括列表中的原始父项,您需要这样做。

    【讨论】:

      猜你喜欢
      • 2021-08-08
      • 2017-01-21
      • 1970-01-01
      • 2016-04-03
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多