使用LINQ的层次数据总和?

时间:2009-06-09 09:58:02

标签: linq hierarchical-data

是否可以使用.NET的LINQ来汇总分层数据?

我的数据类如下所示:

class Node
{
    public decimal Amount;
    public IEnumerable<Node> Children { get; set; }
}

所以我会有一些看起来像这样的数据,但树当然可以任意深入。

var amounts = new Node
{
    Amount = 10;
    Children = new[]
    {
        new Node
        {
            Amount = 20
        },
        new Node
        {
            Amount = 30
        }
    }
};

可以将所有金额相加并通过一个简单的LINQ查询得到结果60?

2 个答案:

答案 0 :(得分:15)

您可以使用更高阶函数来执行此操作:

Func<Node, decimal> summer = null;
summer = node => node.Amount + 
                 (node.Children == null ? 0m : node.Children.Sum(summer));
decimal total = summer(amounts);

请注意,如果您可以确保node.Children永远不会为null,那么summer可以更简单:

summer = node => node.Amount + node.Children.Sum(summer);

或者,您可以使用空合并运算符:

summer = node => node.Amount + 
                 (node.Children ?? Enumerable.Empty<Node>()).Sum(summer);

当然,您可以将其放入单独的方法中:

static decimal SumNodes(Node node)
{
    return node.Amount + 
        (node.Children ?? Enumerable.Empty<Node>())
            .Sum((Func<Node, decimal>)SumNodes);
}

请注意,这里的丑陋是由于方法组转换的模糊性造成的。方法组对类型推断并不太感兴趣。

然后拨打SumNodes(amount)。很多选择:)

第一种形式的完整示例:

using System;
using System.Collections.Generic;
using System.Linq;

class Node
{
    public decimal Amount;
    public IEnumerable<Node> Children { get; set; }
}

public class Test
{
    static void Main()
    {
        var amounts = new Node {
            Amount = 10, Children = new[] {
                new Node { Amount = 20 },
                new Node { Amount = 30 }
            }
        };

        Func<Node, decimal> summer = null;
        summer = node => node.Amount + 
            (node.Children == null ? 0m : node.Children.Sum(summer));

        decimal total = summer(amounts);

        Console.WriteLine(total);
    }
}

我不确定我是否会将这些称为“简单”的LINQ查询,请注意......

答案 1 :(得分:2)

从技术上讲,你可以 write recursive lambda expressions,但你需要疯狂或疯狂地尝试(我还没弄清楚哪个)。但你可以作弊:

    Func<Node, decimal> nodeSum = null;
    nodeSum = node => {
        decimal result = node.Amount;
        if (node.Children != null) {
            result = result + node.Children.Sum(nodeSum);
        }
        return result;
    };
    var value = nodeSum(amounts);