97

Given two IEnumerables of the same size, how can I convert it to a Dictionary using 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 ? */;

And the expected output is:

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

I wonder if there is some simple way to achieve it.

And should I be worried about performance? What if I have large collections?


I don't if there is an easier way to do it, currently I'm doing like this:

I have an Extension method that will loop the IEnumerable providing me the element and the index number.

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++);
        }
    }
}

And I have a method that will loop one of the Enumerables and with the index retrieve the equivalent element on the other 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;
}

Then I use it like:

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);

And the output is correct:

A: Val A
B: Val B
C: Val C
BrunoLM
  • 97,872
  • 84
  • 296
  • 452

5 Answers5

175

With .NET 4.0 (or the 3.5 version of System.Interactive from Rx), you can use Zip():

var dic = keys.Zip(values, (k, v) => new { k, v })
              .ToDictionary(x => x.k, x => x.v);
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
  • Thumbs up for .Zip. Glad it's made it into the BCL. I've had my own version for quite some time. – spender Oct 28 '10 at 01:43
  • 1
    @spender - Even if you made your own, i would take it from [Eric Lipperts Blog](http://blogs.msdn.com/b/ericlippert/archive/2009/05/07/zip-me-up.aspx) – Oliver Oct 28 '10 at 10:48
  • 3
    Pity there is a need for a `Zip` method. If only more statically typed languages would support generic variadic parameters, `Select` would handle this (like `map` in Scheme). – leppie Oct 28 '10 at 10:59
  • 4
    In 2022 it can be shorter as `var dic = keys.Zip(values).ToDictionary(x => x.First, x => x.Second);` – Romulus Urakagi Ts'ai May 19 '22 at 00:47
36

Or based on your idea, LINQ includes an overload of Select() that provides the index. Combined with the fact that values supports access by index, one could do the following:

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

(If values is kept as List<string>, that is...)

dahlbyk
  • 75,175
  • 8
  • 100
  • 122
18

I like this approach:

var dict =
   Enumerable.Range(0, keys.Length).ToDictionary(i => keys[i], i => values[i]);
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • I didn't realize it at first but both `Enumerable` and `Zip` are features of ***Linq***. Food for thought. – Jacksonkr Feb 28 '23 at 17:18
3

If you use MoreLINQ, you can also utilize it's ToDictionary extension method on previously created KeyValuePairs:

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

It also should be noted that using Zip extension method is safe against input collections of different lengths.

Deilan
  • 4,740
  • 3
  • 39
  • 52
1

Starting from .NET Core 3.0, you can just use the improved Zip() :

var dictionary = keys.Zip(values).ToDictionary(x => x.First, x => x.Second);

Define an extension method for this case, and you're as good as you can get :)

spamove
  • 345
  • 3
  • 12