c0rd
c0rd

Reputation: 1419

Selecting List<string> into Dictionary with index

I have a List

List<string> sList = new List<string>() { "a","b","c"};

And currently I am selecting this into a dictionary the following structure:

//(1,a)(2,b)(3,c)
Dictionary<int, string> dResult = new Dictionary<int, string>();
for(int i=0;i< sList.Count;i++)
{
    dResult.Add(i, sList[i]);
}

but with Linq I've seen some slimmer way like ToDictionary((x,index) =>

How is the correct syntax or how can this be solved within one line?

Upvotes: 15

Views: 7949

Answers (4)

Jon Skeet
Jon Skeet

Reputation: 1500675

There's no ToDictionary method that gives this in a single call, as there are no overloads that provide the index, but you can use the Select overload which accepts a Func<T, int, TResult>:

var dictionary = list.Select((value, index) => new { value, index })
                     .ToDictionary(pair => pair.index, pair => pair.value);

Or in C# 7, avoiding creating a bunch of heap-allocated objects using ValueTuple:

var dictionary = list.Select((v, i) => (value: v, index: i))
                     .ToDictionary(pair => pair.index, pair => pair.value);

As of C# 7.1 point release, you can use the inferred ValueTuple literal expression:

var dictionary = list.Select((value, index) => (value, index))
                     .ToDictionary(pair => pair.index, pair => pair.value);

(As noted by Tim, you'll need to adjust the index by 1 to get the original requested output.)

Upvotes: 12

Tim Schmelter
Tim Schmelter

Reputation: 460138

You can use the overload of Select that projects the index to fill an anonymous type:

Dictionary<int, string> dResult = sList
    .Select((s, index) => new { s, index })
    .ToDictionary(x => x.index, x => x.s);

That is the same as what your code does. If you instead want the result you've commented: (1,a)(2,b)(3,c)) you have to add +1 so ToDictionary(x => x.index+1, x => x.s).

Upvotes: 27

MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37000

First you have to get the key and value using the select-overload that also provices the index of the currently used element:

var tmp = sList.Select((x, i) => new { Key = i, Value = x });

Now create the dictionary:

var result = tmp.ToDictionary(x => x.Key, x => x.Value);

Or as a one-liner:

var result = sList.Select((x, i) => new { Key = i, Value = x }).ToDictionary(x => x.Key, x => x.Value);

EDIT: You can also create your own extension-method to also use the syntax you provided:

public static Dictionary<TResultKey, TResultValue> ToDictionary<TSource, TResultKey, TResultValue>(this IEnumerable<TSource> src, Func<TSource, int, TResultKey> KeySelector, Func<TSource, int, TResultValue> ValueSelector) 
{
    var result = new Dictionary<TResultKey, TResultValue>();
    int i = 0;
    foreach (var element in src)
    {
        result[KeySelector(element, i)]  = ValueSelector(element, i);
        i++;
    }
    return result;
}

Now you can call it like this:

List<string> sList = new List<string>() { "a","b","c"};
var result = sList.ToDictionary((x, i) => i + 1, (x, i) => x);

Upvotes: 3

Vitaly Maximov
Vitaly Maximov

Reputation: 121

You can also use this approach without an anonymous type:

Dictionary<int, string> dResult = Enumerable
    .Range(0, sList.Count())
    .ToDictionary(i => i + 1, i => sList[i]);

Upvotes: 4

Related Questions