2008-11-07 4 views
2

J'ai deux listes d'objets personnalisés et je souhaite mettre à jour un champ pour tous les objets d'une liste s'il y a un objet dans l'autre liste qui correspond à une autre paire de champs.Meilleure performance lors de la mise à jour des objets avec linq

Ce code explique mieux le problème et produit les résultats que je veux. Cependant, pour les listes plus grandes 20k, et une liste de 20k avec des objets correspondants, cela prend un temps considérable (31s). Je peux améliorer cela avec ~ 50% en utilisant la méthode Find (Predicate) des listes génériques.

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Collections.Generic; 
namespace ExperimentFW3 
{ 
    public class PropValue 
    { 
     public string Name; 
     public decimal Val; 
     public decimal Total; 
    } 
    public class Adjustment 
    { 
     public string PropName; 
     public decimal AdjVal; 
    } 
    class Program 
    { 
     static List<PropValue> propList; 
     static List<Adjustment> adjList; 

     public static void Main() 
     { 
      propList = new List<PropValue>{ 
       new PropValue{Name = "Alfa", Val=2.1M}, 
       new PropValue{Name = "Beta", Val=1.0M}, 
       new PropValue{Name = "Gamma", Val=8.0M} 
      }; 
      adjList = new List<Adjustment>{ 
       new Adjustment{PropName = "Alfa", AdjVal=-0.1M}, 
       new Adjustment{PropName = "Beta", AdjVal=3M} 
      }; 

      foreach (var p in propList) 
      { 
       Adjustment a = adjList.SingleOrDefault(
        av => av.PropName.Equals(p.Name) 
        ); 
       if (a != null) 
        p.Total = p.Val + a.AdjVal; 
       else 
        p.Total = p.Val; 
      } 
     } 
    } 
} 

Le résultat souhaité est: Alfa total = 2, total = 4, Gamma total = 8

Mais Beta je me demande si cela est possible de faire encore plus vite. Inner joignant les deux listes prend très peu de temps, même lors de la boucle de plus de 20k éléments dans le jeu de résultats. Donc, ma question est de savoir s'il est possible de faire quelque chose comme je le ferais avec T-SQL? Une UPDATE d'une jointure gauche utilisant ISNULL (val, 0) sur la valeur d'ajustement.

Répondre

6

Cette jointure devrait être assez rapide, car il va d'abord parcourir tous les champs adjList pour créer une recherche, puis pour chaque élément dans propList il utilisera simplement la recherche. C'est plus rapide que votre méthode O (N * M) dans le code plus grand - bien que cela puisse facilement être corrigé en appelant ToLookup (ou ToDictionary car vous n'avez besoin que d'une valeur) sur adjList avant la boucle.

EDIT: Voici le code modifié en utilisant ToDictionary. Non testé, vous l'esprit ...

var adjDictionary = adjList.ToDictionary(av => av.PropName); 
foreach (var p in propList) 
{ 
    Adjustment a; 
    if (adjDictionary.TryGetValue(p.Name, out a)) 
    { 
     p.Total = p.Val + a.AdjVal; 
    } 
    else 
    { 
     p.Total = p.Val; 
    } 
} 
+0

Merci pour une réponse rapide! J'ai récemment commencé à regarder linq et j'ai été pris en train d'essayer d'émuler t-sql dans le code. Votre code non testé mesure en ms, j'ai donc eu le coup de pouce de performance que j'ai demandé. – mnsc

+0

Heureux que cela a aidé ... bien que je ne sois pas sûr pourquoi cette Q & A est maintenant un wiki de la communauté :( –

+0

Il y a une case à cocher "community wiki" lors de la publication Parfois elle est activée même si vous n'avez jamais cliqué dessus –

0

Si AdjList pourrait avoir des noms en double, vous devez regrouper les éléments avant de pousser au dictionnaire.

Dictionary<string, decimal> adjDictionary = adjList 
    .GroupBy(a => a.PropName) 
    .ToDictionary(g => g.Key, g => g.Sum(a => a.AdjVal)) 

propList.ForEach(p => 
    { 
    decimal a; 
    adjDictionary.TryGetValue(p.Name, out a); 
    p.Total = p.Val + a; 
    }); 
0

Je sais que je suis annonce la fin, mais je pensais que quelqu'un apprécierait la plus courte réponse plus claire ci-dessous qui gère plusieurs enregistrements par recherche dans AdjList. La création d'une recherche permettra des recherches rapides sur plusieurs éléments et renverra une liste vide s'il n'y a aucun enregistrement dans LookUp.

var adjLookUp = adjList.ToLookUp(a => a.PropName); 
foreach (var p in propList) 
    p.Total = p.Val + adjLookUp[p.Name].Sum(a => a.AdjVal); 
Questions connexes