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.

14
  • 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. Commented Jul 17, 2012 at 13:10
  • 2
    toDictionary() will always create an instance of your Dictionary, so you dont need to initialize it in the first place. Commented Jul 17, 2012 at 13:10
  • I would think you have to write an extension method for this. Is that what you're looking for? Commented Jul 17, 2012 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. Commented Jul 17, 2012 at 13:11
  • 2
    but i do not want to iterate over array. Do you think ToDictionary does it with magic? Commented Jul 17, 2012 at 13:24

7 Answers 7

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
}
Sign up to request clarification or add additional context in comments.

Comments

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

Comments

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

Comments

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

1 Comment

I find it a bit surprising you're encouraging a beginner programmer who doesn't understand dictionaries and arrays to use a post-increment!
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().

Comments

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)

Comments

1
for (int i = 0; i < str_array.Length; i++)
{
    dict[i] = str_array[i];
}

2 Comments

What is s? Why is i declared outside the for?
s is no more. And you're right i should be scoped more tightly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.