6
votes

Trouver une valeur dans une liste d'objets de dictionnaire

J'ai ce qui peut potentiellement être une très grande liste d'objets de dictionnaires que je devrai trouver une valeur particulière d'une clé. Je peux certainement faire quelque chose comme xxx

et cela va bien itérair à FOO jusqu'à ce qu'il trouve la clé ou la fin des extrémités.

Pour accélérer cela, je préférerais utiliser une petite quantité de linq. Si c'était une liste normale, je vais bien, mais comme il s'agit d'une liste d'objets de dictionnaire, je ne suis pas trop sûr de savoir comment c'est fait.

aide ou conseil apprécié. < / p>


7 commentaires

Voulez-vous rendre le code plus rapide ou plus court? Votre code est déjà aussi rapide que vous pouvez obtenir.


Notez que Linq ne placent pas vraiment très agréable avec les paramètres , donc non seulement l'utilisation de Linq le rendit (marginalement) plus lent, il serait probablement plus laid. Le seul problème réel avec votre code est que vous n'avez aucun moyen de savoir si vous avez rompu hors de la boucle ou si vous ne pouviez pas trouver la clé dans un dictionnaire.


La clé ne contient que dans l'un des dictionnaires de FOO? Ou peut-il être dans plusieurs dictionnaires?


Je suis d'accord avec les autres. Votre code est (a) court, (b) lisible et (c) aussi efficace que possible. Je laisserais ça seul! (Je suppose que nom est initialisé à NULL afin que vous sachiez que cela n'a pas été trouvé?)


Dans quelle mesure vous avez besoin de trouver?


Combien de dictionnaires avez-vous et où sont-ils peuplés? Si vous avez un goulot d'étranglement et Ceci est l'endroit où (à ce point: avez-vous mesuré? ), vous ferez probablement plus d'high-esprit de traiter avec la création / la création des dictionnaires eux-mêmes. Créez un meilleur design , et vous pouvez alors arriver à une meilleure solution ici .


Je ne pense pas que l'on puisse indiquer que ce code est déjà aussi rapide que possible sans connaître la quantité de clés à FOO. Il pourrait facilement y avoir un avantage dans l'utilisation d'un peu de filetage ici - par ex. via plinq ou une mise en œuvre "manuelle".


4 Réponses :


3
votes

Ce code sera plus court, mais prendra juste un peu plus long:

var dictWithKey = Foo.First(d => d.ContainsKey(key));
name = dictWithKey[key];


2 commentaires

Remarque Cela lancera une exception si la clé n'est pas dans aucun dictionnaire; Le code de l'OP ne le fait pas.


C'est vrai. L'OP n'était pas clair sur les attentes valides.



6
votes

Je pense que vous avez écrit la version la plus efficace de ce que vous voulez faire. Depuis que LINQ ne joue pas bien avec les paramètres de sortie, il faudra un peu plus longtemps. Mais voici comment vous le feriez:

var dict = Foo.FirstOrDefault(d => d.ContainsKey(key));
if (dict != null) { dict.TryGetValue(key, out name); }


5 commentaires

Remarque Cela lancera une exception si la clé n'est pas dans aucun dictionnaire; Le code de l'OP ne le fait pas.


Je l'ai réparé afin qu'il ne jette plus une erreur lorsque la clé n'est pas dans le dictionnaire


Merci pour celui-ci. C'est une liste simple > qui pourrait avoir quelque chose jusqu'à 60 000 objets de dictionnaire. J'étais ignorant que Linq n'était pas un lapin heureux sans valeurs.


Lire Eric Lippert sur la raison pour laquelle les paramètres et Linq ne mixent pas: blogs.msdn.com/b/ericlippert/archive/2012/08/14/...


Hmm, ne devrait-il pas être: si (dict! = Null) à la place? Dans le code actuel, vous invoquez trygetvalue uniquement si le dict est null, causant une nullrefException.



-1
votes

Vous avez la configuration de la clé comme type long.

long key = 1;
Dictionary<long, string> name = foo.Where(d => d.ContainsKey(key)).First();


2 commentaires

1) Ceci ne regarde que par un dictionnaire, pas une liste d'entre eux. 2) cela fait une recherche linéaire; Les dictionnaires sont spécifiquement conçus pour être recherchés efficacement; Vous ne le permettez pas.


J'ai raté que c'était une liste d'objets de dictionnaire. Corrigé maintenant.



0
votes

Eh bien, en fonction de la quantité de dictionnaires dans foo code> peut y avoir un avantage dans l'utilisation de Linq parallèle (plinq) à la place:

string name = null;
Parallel.ForEach(foo, f =>
                      {
                          if (name != null)
                              return;

                          if (f.ContainsKey(key))
                              name = f[key];
                      });
  • plinq: 89ms li>
  • Solution originale: 250ms LI> ul>

    Cependant, je tiens à souligner que Plinq n'est pas nécessairement toujours la réponse à la recherche de choses plus rapidement. Dans certains cas où vous avez du code qui utilise des boucles parallèles pour les boucles pour traverser peu d'éléments, le coût réel du démarrage des threads en arrière-plan est effectivement beaucoup plus coûteux que celui d'itération à l'aide d'une simple boucle - donc, utilisez-le quand il y en a beaucoup éléments à itérer à travers :) p> p>


6 commentaires

La seule façon dont ces deux extraits de code compileraient est s'il y a une variable scopée à la boucle ou au-dessus de la boucle parallèle, les résultats vont donc se battre entre eux pour qui gagne, et il est peu probable que vous obteniez le résultat correct.


@Servy si la clé de clé de la même valeur ou de la clé est unique sur tous les dictionnaires, comment cela peut-il donner un mauvais résultat? :) Si la clé de recherche est utilisée dans plusieurs dictionnaires et que des cartes de différentes valeurs, alors oui, il y aura une course (mais aussi loin que je peux le dire, l'OP n'a pas dit cela quelque part?)


Tous les paramètres doivent être définis dans le corps de la méthode; Cela signifie que chaque dictionnaire qui ne trouve pas de jeu de valeur nom pour être null (ou théoriquement une autre valeur sans signification, mais dans la pratique, ce sera la valeur par défaut). Donc, selon ce qui est exécuté en dernier, c'est ce qui permettra la valeur finale du nom . Si c'est un dictionnaire qui a une valeur, cela fonctionnera. Si un dictionnaire sans valeur n'est exécuté plus tard, cette valeur sera éliminée.


@Servy est à 100% à droite - je n'ai pas tenu compte du fait que le paramètre out> du paramètre doit être défini. Merci d'avoir fait remarquer cela. J'ai mis à jour l'échantillon de code et le texte.


Vous faites maintenant la recherche deux fois chaque fois que vous avez trouvé une valeur. Votre problème réel est en fait que vous n'élevez pas correctement les résultats de chaque itération; Vous définissez une variable partagée et cela n'est tout simplement pas approprié. Au lieu de cela, chaque itération génère, il s'agit entièrement du résultat local, puis comptez sur les techniques de cadre de parallélisation appropriées pour les agréger. IE var cheature = foo.asparallel (). Premier (DIC => DIC.Containskey (clé)) [Touche]; (Pour une bonne gestion des erreurs, utilisez le code d'utilisation en op avec le Asparallall ajouté dans.)


Je vois ton point et je pense que c'est valide. En termes de référence, je veux juste ajouter que votre suggestion me donne en moyenne une exécution à 140 ms. C'est un peu plus cher que ma solution plinq. Je sais que la recherche est faite deux fois, mais puisque cette opération est assez bon marché, cela n'affecte pas beaucoup de manière significative. Quoi qu'il en soit, merci de votre contribution, je suis heureux que vous ayez pris votre temps pour expliquer où les choses peuvent être améliorées.