2009-05-22 11 views
5

Je suis actuellement en train d'utiliser le code suivant pour y parvenir, mais il semble qu'il devrait y avoir quelque chose de mieux ... des suggestions? Il me semble simplement qu'il devrait y avoir un moyen de sauter le foreach ...Lorsque vous utilisez une clause LINQ Where dans un dictionnaire, comment puis-je retourner un dictionnaire du même type?

Dictionary<string,string> getValidIds(Dictionary<string,string> SalesPersons,List<string> ids) 
{ 
    Dictionary<string,string> o = new Dictionary<string,string>(); 
    var ie = SalesPersons.Where<KeyValuePair<string, string>>(t => ids.Contains(t.Key)); 
    foreach (var i in ie) 
    { 
     o.Add(i.Key, i.Value); 
    } 
    return o; 
} 

Répondre

5

semble être beaucoup d'épiloguer sur la recherche des choses dans la liste. Si la liste ne contient que quelques éléments, alors rien de grave. Si la liste contient des milliers d'éléments, vous devez y rechercher O (1). HashSet peut fournir ceci.

Dictionary<string, string> getValidIds(
    Dictionary<string, string> SalesPersons, 
    List<string> ids 
) 
{ 
    HashSet<string> idsFast = new HashSet<string>(ids); 
    Dictionary<string, string> result = SalesPersons 
    .Where(kvp => idsFast.Contains(kvp.Key)) 
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value) 
    return result; 
} 
9

sûr que vous pouvez simplement appeler ToDictionary sur le résultat de l'appel Où:

Dictionary<string, string> GetValidIds(Dictionary<string, string> salesPersons, 
    IList<string> ids) 
{ 
    return salesPersons 
     .Where(p => ids.Contains(p.Key)) 
     .ToDictionary(p => p.Key, p => p.Value); 
} 
+0

Voir ma réponse pour les notes de performance ... –

+0

+1, Marc, bien que je pense que si vous êtes inquiet au sujet de cette perf genre de chose que vous avez beaucoup trop en mémoire comme il est! :) –

+1

@Matt ... bien, peut-être - mais il y a un point où O (1)/O (N * log (N)) * n'est pas * déraisonnable, mais O (N^2) * est * un * problème - que ce soit 1k, 10k ou 100k est à la hauteur du processus spécifique, bien sûr. –

6

Fait intéressant, si vous énumérer sur le dictionnaire (longueur N) d'abord et vérifiez la liste (longueur M) pour l'inclusion, puis vous obtenez des performances O (NM).

Vous pouvez créer un HashSet<> des ID, mais cela semble redondant puisque nous avons déjà un dictionnaire (pré-haché) disponible.

Je voudrais plutôt itérer sur les identifiants d'abord; puisque la recherche par dictionnaire (par clé) est O (1), cela donne des performances O (M) - cela peut toutefois signifier que vous n'utilisez pas LINQ (puisque TryGetValue n'aimerait pas LINQ (et l'introduction d'un tuple est trop dur comme travail) ...

Dictionary<string, string> getValidIds(
      IDictionary<string, string> salesPersons, 
      IEnumerable<string> ids) { 
     var result = new Dictionary<string, string>(); 
     string value; 
     foreach (string key in ids) { 
      if (salesPersons.TryGetValue(key, out value)) { 
       result.Add(key, value); 
      } 
     } 
     return result; 
    } 

il ne pas trop me regarde que ce soit plus de lignes que la version LINQ, il supprime un O (N) de complexité ...


Edit, le pourrait travailler (je ne l'ai pas testé), mais je pense que c'est un abus de LINQ, et certainement pas à l'échelle de PLINQ etc ... utiliser avec une extrême prudence !! Je crois aussi l'approche foreach a simplement moins de frais généraux, donc sera plus rapide ... de toute façon:

Dictionary<string, string> getValidIds(
     IDictionary<string, string> salesPersons, 
     IEnumerable<string> ids) 
    { 
     string value = null; 
     return (from key in ids 
       where salesPersons.TryGetValue(key, out value) // HACK: v. dodgy 
       select new { key, value }) 
       .ToDictionary(x=>x.key, x=>x.value); 
    } 
Questions connexes