4

是否可以使用 .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?

4

2 回答 2

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);

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

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 查询,请注意……

于 2009-06-09T10:03:27.683 回答
2

从技术上讲,您可以 编写递归 lambda 表达式,但您需要非常聪明或非常聪明才能尝试(我还没弄清楚是哪个)。但你可以作弊:

    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);
于 2009-06-09T10:06:22.810 回答