Iterate over C# dictionary's keys with index?

31,494

Solution 1

There's no such concept as "the index of the key". You should always treat a Dictionary<TKey, TValue> as having an unpredictable order - where the order which you happen to get when iterating over it may change. (So in theory, you could add one new entry, and the entries could be in a completely different order next time you iterated over them. In theory this could even happen without you changing the data, but that's less likely in normal implementations.)

If you really want to get the numeric index which you happened to observe this time, you could use:

foreach (var x in dictionary.Select((Entry, Index) => new { Entry, Index }))
{
    Console.WriteLine("{0}: {1} = {2}", x.Index, x.Entry.Key, x.Entry.Value);
}

... but be aware that that's a fairly misleading display, as it suggests an inherent ordering.

From the documentation:

For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair<TKey, TValue> structure representing a value and its key. The order in which the items are returned is undefined.

EDIT: If you don't like the Select call here, you could create your own extension method:

public struct IndexedValue<T>
{
    private readonly T value;
    private readonly int index;

    public T Value { get { return value; } }
    public int Index { get { return index; } }

    public IndexedValue(T value, int index)
    {
        this.value = value;
        this.index = index;
    }
}

public static class Extensions
{
    public static IEnumerable<IndexedValue<T>> WithIndex<T>
        (this IEnumerable<T> source)
    {
        return source.Select((value, index) => new IndexedValue<T>(value, index));
    }
}

Then your loop would be:

foreach (var x in dictionary.WithIndex())
{
    Console.WriteLine("{0}: {1} = {2}", x.Index, x.Value.Key, x.Value.Value);
}

Solution 2

Technically, the key is the index in a Dictionary<TKey, TValue>. You're not guaranteed to get the items in any specific order, so there's really no numeric index to be applied.

Solution 3

Not really. Note that keys in a dictionary are not logically "ordered". They don't have an index. There is no first or last key, from the Dictionary's point of view. You can keep track on your own whether this is the first key returned by the enumerator, as you are doing, but the Dictionary has no concept of "give me the 5th key", so you couldn't use a for loop with an indexer as you could with a list or array.

Share:
31,494
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

    How do I iterate over a Dictionary's keys while maintaining the index of the key. What I've done is merge a foreach-loop with a local variable i which gets incremented by one for every round of the loop.

    Here's my code that works:

    public IterateOverMyDict()
    {
        int i=-1;
        foreach (string key in myDict.Keys)
        {
            i++;
            Console.Write(i.ToString() + " : " + key);
        }
    }
    

    However, it seems really low tech to use a local variable i. I was wondering if there's a way where I don't have to use the "extra" variable? Not saying this is a bad way, but is there a better one?

  • Admin
    Admin about 11 years
    The order doesn't matter to me, and I don't need the TValue at the moment, I just need to print the keys with some kind of index in front of them so user can then type select 1 and it will select the key with index of 1 (the key with 1 in front of it in the printed version, that is.)
  • Jon Skeet
    Jon Skeet about 11 years
    @MarkusMeskanen: So why did you ask a question in terms of "while maintaining the index of the key"? Anyway, the LINQ query I've shown you should help.
  • Admin
    Admin about 11 years
    Apologies for not speaking English as my native language, I have no idea how should I ask it if that's not clear enough. :/ However, that's why I gave you the code I'm using atm, as an example to show I'm not using TValue at all and I'm only printing the key and an int in front of it. And with all respect, that LINQ looks terrible and I'd rather use my own solution, thanks for your answer tho :)
  • Jon Skeet
    Jon Skeet about 11 years
    @MarkusMeskanen: Just explaining that you knew the order was unstable (and why you want such a misleading display) would have been helpful. I don't see what's wrong with the LINQ - given that you need to get an extra value from somewhere, what possible other solution would you have expected? (You could write a method to "index" any sequence, but via a custom struct, I suppose...)
  • Admin
    Admin about 11 years
    I don't need them in any specific order, I just need to iterate through the keys and print them with an int from 1 to Keys.Count in front of them