2009-06-09 7 views
4

Est-il possible d'additionner des données hiérarchiques en utilisant le LINQ de .NET?Somme des données hiérarchiques utilisant LINQ?

Ma classe de données ressemble à ceci:

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

J'éprouverez des données ressemble à cela, mais l'arbre pourrait bien sûr être arbitrairement profond.

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

Il est possible de faire la somme de tous les montants et d'obtenir le résultat 60 avec une simple requête LINQ?

Répondre

2

Techniquement vous pouvezwrite recursive lambda expressions, mais vous devez être fou ou incroyablement brillant à essayer (je n'ai pas compris lequel). Mais vous pouvez tricher:

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

Vous pouvez le faire avec une fonction d'ordre supérieur:

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

Notez que si vous pouvez vous assurer que node.Children ne sera jamais nulle, l'été peut être plus simple:

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

Alternativement, vous pouvez utiliser l'opérateur de coalescence null:

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

Bien sûr, vous pourriez mettre cela dans un autre méthode à la place:

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

Notez que la laideur est due à une ambiguïté dans les conversions de groupes de méthodes. Les groupes de méthodes n'ont pas beaucoup d'amour dans l'inférence de type.

puis d'appeler SumNodes(amount). Beaucoup d'options :)

Exemple complet de la première forme:

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

Je ne suis pas sûr que je dirais l'un de ces un « simple » requête LINQ, vous l'esprit ...

+0

Jon, je l'avouerais encore quelques fois, mais je ne le laisserai pas. –

+0

Strictement, c'est plus d'une "fonction d'ordre supérieur" que d'un vrai "lambda récursif" ... –

+0

Ce n'était pas juste jusqu'à ce que je l'ai réparé :) –

Questions connexes