2010-10-28 15 views
48

Étant donné deuxIEnumerable s de la même taille, comment puis-je le convertir en unDictionaryLINQ?Carte deux listes dans un dictionnaire en C#

IEnumerable<string> keys = new List<string>() { "A", "B", "C" }; 
IEnumerable<string> values = new List<string>() { "Val A", "Val B", "Val C" }; 

var dictionary = /* Linq ? */; 

Et le résultat attendu est:

A: Val A 
B: Val B 
C: Val C 

Je me demande s'il y a un moyen simple pour y parvenir.

Et devrais-je m'inquiéter de la performance? Et si j'ai de grandes collections?


Je ne sais pas s'il y a un moyen plus facile de le faire, actuellement, je fais comme ceci:

J'ai une méthode d'extension qui bouclera le IEnumerable me fournissant l'élément et l'indice nombre.

public static class Ext 
{ 
    public static void Each<T>(this IEnumerable els, Action<T, int> a) 
    { 
     int i = 0; 
     foreach (T e in els) 
     { 
      a(e, i++); 
     } 
    } 
} 

et moi avons une méthode qui boucle l'un des Enumerables et l'indice de récupérer l'élément équivalent de l'autre Enumerable.

public static Dictionary<TKey, TValue> Merge<TKey, TValue>(IEnumerable<TKey> keys, IEnumerable<TValue> values) 
{ 
    var dic = new Dictionary<TKey, TValue>(); 

    keys.Each<TKey>((x, i) => 
    { 
     dic.Add(x, values.ElementAt(i)); 
    }); 

    return dic; 
} 

Je l'utiliser comme:

IEnumerable<string> keys = new List<string>() { "A", "B", "C" }; 
IEnumerable<string> values = new List<string>() { "Val A", "Val B", "Val C" }; 

var dic = Util.Merge(keys, values); 

Et la sortie est correcte:

A: Val A 
B: Val B 
C: Val C 

Répondre

101

Avec .NET 4.0 (ou la version 3.5 de System.Interactive de Rx), vous pouvez utiliser Zip():

var dic = keys.Zip(values, (k, v) => new { k, v }) 
       .ToDictionary(x => x.k, x => x.v); 
+0

Pouce pour .Zip. Heureux que ça soit dans la BCL. J'ai eu ma propre version depuis un certain temps. – spender

+1

@spender - Même si vous avez fait votre propre, je le prendrais de [Eric Lipperts Blog] (http://blogs.msdn.com/b/ericlippert/archive/2009/05/07/zip-me-up. aspx) – Oliver

+1

Dommage qu'il y ait un besoin pour une méthode 'Zip'. Si seulement plus de langages statiquement typés supportaient des paramètres variadiques génériques, 'Select' gèrerait ceci (comme' map' dans Scheme). – leppie

20

Ou basé sur votre idée, LINQ comprend une surcharge de Select() qui fournit l'index. Combiné avec le fait que values soutient l'accès par index, on peut effectuer les opérations suivantes:

var dic = keys.Select((k, i) => new { k, v = values[i] }) 
       .ToDictionary(x => x.k, x => x.v); 

(Si values est conservé comme List<string>, qui est ...)

+0

Oh, belle! Je cherchais exactement ça. Linq est pratique, mais le code dont vous avez besoin pour écrire semble vraiment confus. – Nyerguds

7

J'aime cette approche:

var dict = 
    Enumerable.Range(0, keys.Length).ToDictionary(i => keys[i], i => values[i]); 
0

Si vous utilisez MoreLINQ, vous pouvez aussi l'utiliser est ToDictionary méthode d'extension sur précédemment créé KeyValuePair s:

var dict = Enumerable 
    .Zip(keys, values, (key, value) => KeyValuePair.Create(key, value)) 
    .ToDictionary(); 

Il convient également de noter que l'utilisation de la méthode d'extension Zip est sûre contre les collections d'entrée de différentes longueurs.