2009-06-09 5 views
4

Можно ли суммировать иерархические данные с помощью LINQ .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 
     } 
    } 
}; 

Возможно суммировать все суммы и получить результат 60 с помощью одного простого запроса LINQ?

ответ

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

Вы можете сделать это с помощью функций высшего порядка:

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

Обратите внимание, что если вы можете гарантировать, что node.Children никогда не будет аннулирована, лето может быть проще:

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 запрос, заметьте ...

+0

Jon, я бы поднял это еще несколько раз, но SO не позволит мне. –

+0

Строго говоря, это скорее «функция более высокого порядка», чем истинная «рекурсивная лямбда» ... –

+0

Это было не так, пока я не исправил ее :) –

Смежные вопросы