1

I have a string array with strings and a dictionary.

String [] str_array;
Dictionary<int, string> dict = new Dictionary<int, string>();

dict = str_array.toDictionary();

How do I get my array into a dictionary with int and String? I have seen all those examples with bool, but I need int and string.

The int (key) would be just the actual position of the dictionary (dict.count) and the value would be the value of the array at that position.

edit: thx to all answers but I do not want to iterate over array. I assume that with array.toDictionary the performance would be better than iterating over array and just assigning the value of the array to a dictionary. Arrays might have 5k elements.

edit2: the reason is that i have to pass a dictionary to a method...its REQUIRED. and all my values are within a simple array.

edit3: The most important thing is performance. Maybe iterating over array and assigning values to dict is faster than array.toDictionary, but the problem is that i do not have that little piece of code to benchmark both.

Gero
  • 12,993
  • 25
  • 65
  • 106
  • Try iterating over the array using a for loop and then add the items inside the loop to the dictionary using the index in the loop as your key and the string value from the array as your dictionary value. – tsells Jul 17 '12 at 13:10
  • 2
    `toDictionary()` will always create an instance of your Dictionary, so you dont need to initialize it in the first place. – Tim Schmelter Jul 17 '12 at 13:10
  • I would think you have to write an extension method for this. Is that what you're looking for? – Pete Jul 17 '12 at 13:10
  • You could do it manually, just foreach through the string array and then add each string to the dictionary. Use an int variable that is incremented each pass through the loop as the key. – Purplegoldfish Jul 17 '12 at 13:11
  • 2
    `but i do not want to iterate over array.` Do you think `ToDictionary` does it with magic? – L.B Jul 17 '12 at 13:24
  • I am afraid of magic. I hope its something like in Star Trek with the replicator. You state your wish and get 10 sunday chocolate milkshakes. Because of the bad mood. Like i said, i thought/assume that toDictionary might be faster than iteration over array. – Gero Jul 17 '12 at 13:30
  • All programs come down to loops and `if`s ultimately! – Joe Jul 17 '12 at 13:45
  • @Gero - My answer might now be more helpful; a benchmark and another suggestion that might be helpful. – Andras Zoltan Jul 17 '12 at 14:07
  • @L.B I would assume `ToDictionary` would do it with highly-optimized low-level (IL) code, hopefully taking advantage of specialized processor instructions to run some of the operations in parallel.  **So yes, magic.** – Slipp D. Thompson Apr 24 '14 at 11:03
  • @SlippD.Thompson Don't be so sure about the things you **assume**. I expect you to post some reference links so that we all can understand this optimized magic (Or better, don't comment unnecessarily) – L.B Apr 24 '14 at 21:14
  • @L.B You misunderstand.  This assumption comes from working with many different languages and toolkits over the years, and seeing what tends to happen when a common library is torture-tested and run over by many talented devs.  Whether or not .NET actually does this is not my point; however, if that's a topic that interests you, you're (now) free to peruse the [.NET source code for `System.Linq.Enumerable.ToDictionary<>()`](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs#c7f3fa13329a79cd). _(oh look! it's a just foreach loop!)_ – Slipp D. Thompson Apr 25 '14 at 04:12
  • @SlippD.Thompson What are you trying to say? I don't get you. My very first comment exactly says what your link says. `Do you think ToDictionary does it with magic?` So there is no such thing like `I would assume ToDictionary would do it with highly-optimized low-level (IL) code, hopefully taking advantage of specialized processor instructions to run some of the operations in parallel` – L.B Apr 25 '14 at 18:06
  • @L.B Read it again then. **Important: “Whether or not .NET actually does this is not my point”** – Slipp D. Thompson Apr 25 '14 at 18:25
  • @SlippD.Thompson Sorry I don't understand what your point is, what you are trying to say. Or what was wrong with my comment? In fact, I can't say that I am much interested in your response. – L.B Apr 25 '14 at 19:48

7 Answers7

8

First - your problem over performance is interesting and smacks of premature optimisation - as this answer will show, you're probably looking at a couple of milliseconds difference in performance between a for loop and ToDictionary.

Unless you are running this in a realtime system I can't see much of a problem.

On to the show - the following is a crude benchmark (only real-world timings are reliable) of three (and a half) different ways I can think of to build the dictionary. The first uses the for loop, with the second doing the same but not using the array's Length property (just for interest); the third and fourth use ToDictionary; one uses a Select and one uses a counter variable (a hybrid):

[TestMethod]
public void SomeBenchmark()
{
    List<double> forLoopTimes = new List<double>();
    List<double> forLoop2Times = new List<double>();
    List<double> toDictionaryTimes = new List<double>();
    List<double> hybridTimes = new List<double>();

    string[] array = Enumerable.Range(0, 5000).Select(i => i.ToString()).ToArray();

    Dictionary<int, string> dictionary;

    int runCount = 5000;
    int arrayLen = array.Length;

    while (runCount-- != 0)
    {
        Stopwatch sw = Stopwatch.StartNew();
        dictionary = new Dictionary<int, string>();
        for (int i = 0; i < array.Length; i++)
        {
            dictionary[i] = array[i];
        }
        sw.Stop();
        forLoopTimes.Add(sw.Elapsed.TotalMilliseconds);

        sw.Restart();
        dictionary = new Dictionary<int, string>();
        for (int i = 0; i < arrayLen; i++)
        {   //same as before - but using arrayLen instead of property
            dictionary[i] = array[i];
        }
        sw.Stop();
        forLoop2Times.Add(sw.Elapsed.TotalMilliseconds);

        sw.Restart();
        dictionary = array.Select((s, i) => new { Key = i, Value = s }).ToDictionary(v => v.Key, v => v.Value);
        sw.Stop();
        toDictionaryTimes.Add(sw.Elapsed.TotalMilliseconds);

        int counter = 0;
        sw.Restart();
        dictionary = array.ToDictionary(s => counter++, s => s);
        sw.Stop();
        hybridTimes.Add(sw.Elapsed.TotalMilliseconds);
    }
    Console.WriteLine("for loop average: {0} milliseconds", forLoopTimes.Average());
    Console.WriteLine("for loop(2) average: {0} milliseconds", forLoop2Times.Average());
    Console.WriteLine("ToDictionary average: {0} milliseconds", toDictionaryTimes.Average());
    Console.WriteLine("Hybrid average: {0} milliseconds", hybridTimes.Average());
}

Results (Release build, and takes about 20 seconds to run on my Dell 2.4Ghz Workstation):

For loop average: 0.28880804 milliseconds

For loop(2) average: 0.2773845 milliseconds

ToDictionary average: 0.479094339999998 milliseconds

Hybrid average: 0.353655779999999 milliseconds

So for loop is undeniably faster - by at least 22% of the closest ToDictionary implementation. I've tried it with 100,000 elements and it gets up to about 30% then.

Note the second for loop result - seems to suggest that bypassing the Length property is a good idea. Indeed I've done 4 runs in a row and these are the results (including the first, from above):

For loop: 0.28880804, 0.28562478, 0.283770739999999, 0.287241679999999

For loop(2): 0.2773845, 0.27621306, 0.27869996, 0.27962916

ToDictionary: 0.479094339999998, 0.476417939999997, 0.476162219999997, 0.475776479999997

Hybrid: 0.353655779999999, 0.3583224, 0.352022739999998, 0.349865779999999

However I have seen the results flip for at least one benchmark result, too - demonstrating how largely pointless this kind of benchmarking can be. Realistically we should be generating a different array for each test, too, to avoid caching etc.

There is an alternative.

If the method you're calling accepts an IDictionary<int, string> (note - the interface); and not a Dictionary<int, string> you can create a simple wrapper type that implements the necessary members of the interface, thus sidestepping the need to project into a Dictionary at all; so long as only certain members are required. Here's an almost complete implementation:

public class FakeDictionary : IDictionary<int, string>
{
    private readonly string[] _array;

    public FakeDictionary(string[] array)
    {
        _array = array;
    }

    #region IDictionary<int,string> Members

    public void Add(int key, string value)
    {
        throw new NotSupportedException();
    }

    public bool ContainsKey(int key)
    {
        return key >= 0 && key < _array.Length;
    }

    public ICollection<int> Keys
    {
        get { return Enumerable.Range(0, _array.Length).ToArray(); }
    }

    public bool Remove(int key)
    {
        throw new NotSupportedException();
    }

    public bool TryGetValue(int key, out string value)
    {
        value = null;
        if (key >= 0 && key < _array.Length)
        {
            value = _array[key];
            return true;
        }
        return false;
    }

    public ICollection<string> Values
    {
        get { return _array; }
    }

    public string this[int key]
    {
        get
        {
            try
            {
                return _array[key];
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new KeyNotFoundException("Invalid key", ex);
            }
        }
        set //note - can't be used to add items
        {
            try
            {
                _array[key] = value;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new KeyNotFoundException("Invalid key", ex);
            }
        }
    }

    #endregion

    #region ICollection<KeyValuePair<int,string>> Members

    public void Add(KeyValuePair<int, string> item)
    {
        throw new NotSupportedException();
    }

    public void Clear()
    {
        throw new NotSupportedException();
    }

    public bool Contains(KeyValuePair<int, string> item)
    {
        return ContainsKey(item.Key) && _array[item.Key].Equals(item.Value);
    }

    public void CopyTo(KeyValuePair<int, string>[] array, int arrayIndex)
    {
        //too much for an SO answer.
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _array.Length; }
    }

    public bool IsReadOnly
    {
        //technically it's not - because we can modify individual elements - 
        //but at the collection-level it is
        get { return true; }
    }

    public bool Remove(KeyValuePair<int, string> item)
    {
        throw new NotSupportedException();
    }

    #endregion

    #region IEnumerable<KeyValuePair<int,string>> Members

    public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}
Community
  • 1
  • 1
Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
4

This does what you want even if it doesn't make sense to me to create a Dictionary with a key that is just an index of an Array.

Dictionary<int, string> dict = str_array
    .Select((s, i) => new { S = s, Index = i})
    .ToDictionary(x => x.Index, x => x.S);

The indexer of an Array (Or List) is (at least) just as quick as looking up a dictionary key.

https://stackoverflow.com/a/908055/284240

Community
  • 1
  • 1
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
2

It would be better to just use foreach or for as it would be easier to read and have good performance over LINQ (I would think). But this answer is just to provide another way of using Enumerable.ToDictionary :-)

  Dictionary<int, string> dict;
  dict = Enumerable.Range(0, str_array.Length).ToDictionary(i => i, i => str_array[i]);

And here's a short one:

  int i = 0; // start key
  Dictionary<int, string> dict;
  dict = str_array.ToDictionary(s => ++i);
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
1
for (int i = 0; i < str_array.Length; i++)
{
    dict[i] = str_array[i];
}
Joe
  • 46,419
  • 33
  • 155
  • 245
1

It's hard to think why would you need something like this, since you could think of an array as if it already is a dictionary with an int key and string values.

But you could do it like this:

int i = 0;
foreach (string s in str_array)
{
    dict.Add(i++, s);
}
Pablo Santa Cruz
  • 176,835
  • 32
  • 241
  • 292
  • I find it a bit surprising you're encouraging a beginner programmer who doesn't understand dictionaries and arrays to use a post-increment! – Joe Jul 17 '12 at 13:12
1
foreach(string s in str_array)
    dict.Add(dict.Count, s);

Something like that, as a simple example?

Alternatively, define an extension method along these lines:

public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> source)
{
    Dictionary<int, T> result = new Dictionary<int, T>();
    foreach(T item in source)
        result.Add(result.Count, item);
}

And use it by calling str_array.ToDictionary().

Kristian Fenn
  • 867
  • 5
  • 14
1

If you are looking to make it simple, you may use an Extension Method.

public static class ExtensionMethod 
{
    public static IDictionary<int, string> ToDictionary(this string[] array)
    {
        return array
            .Select((k, v) => new { Key = k, Value = v})
            .ToDictionary(x => x.Key, x => x.Value);
    }
}

And then you can use it in your program:

String [] str_array;
Dictionary<int, string> dict = new Dictionary<int, string>();

dict = str_array.ToDictionary();

Please note you need to worry about LinQ performance over foreach only if you are experiencing performance problems. Only huge operations will be different in performance.

See also "Nested foreach" vs "lambda/linq query" performance(LINQ-to-Objects)

Community
  • 1
  • 1
Gustavo Gondim
  • 1,635
  • 2
  • 18
  • 39