97 votes

Convertir deux listes en un dictionnaire en C#

Étant donné deux IEnumerable s de la même taille, comment puis-je le convertir en une Dictionary en utilisant Linq ?

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 existe un moyen simple d'y parvenir.

Et dois-je m'inquiéter des performances ? Que faire 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 ça :

J'ai une méthode d'extension qui va boucler le IEnumerable en me fournissant l'élément et le numéro d'index.

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 j'ai une méthode qui va boucler l'un des Enumerables et avec l'index récupérer l'élément équivalent sur 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;
}

Alors je l'utilise 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

175voto

dahlbyk Points 24897

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 votes

Bravo pour le .Zip. Je suis heureux qu'il ait été intégré à la BCL. J'ai ma propre version depuis un certain temps.

1 votes

@spender - Même si vous avez fait le vôtre, je l'aurais pris de Blog d'Eric Lipperts

3 votes

Dommage que l'on ait besoin d'un Zip méthode. Si seulement les langages plus typés statiquement pouvaient supporter les paramètres génériques variadiques, Select s'en chargerait (comme map dans le schéma).

36voto

dahlbyk Points 24897

Ou selon votre idée, LINQ inclut une surcharge de Select() qui fournit l'indice. Si l'on ajoute à cela le fait que values supporte l'accès par index, on pourrait faire ce qui suit :

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> c'est-à-dire...)

1 votes

Oh, magnifique ! C'est exactement ce que je cherchais. Linq est pratique, mais le code que vous devez écrire avec est vraiment confus IMO.

18voto

Todd Menier Points 3599

J'aime cette approche :

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

3voto

Deilan Points 76

Si vous utilisez PlusLINQ vous pouvez également utiliser son ToDictionary sur une méthode d'extension créée précédemment KeyValuePair s :

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X