开发者

"Peeking Ahead" while looping through Dictionary Items

开发者 https://www.devze.com 2022-12-10 07:55 出处:网络
This seems like it should be something very easy to do, but every time I approach this issue, I end up w/ solutions that feel \"less than elegant\"

This seems like it should be something very easy to do, but every time I approach this issue, I end up w/ solutions that feel "less than elegant"

Here is my basic question: If I am looping through a dictionary that I have ordered in a particular way, within any given point in the loop how can I "peek" or get reference to a dictionary item 'x' places ahead of the current item without changing the current enumerator? For instance:

Dim tempDictionary = New Dictionary(Of String, String)

tempDictionary.Add("TestName1", "TestValue1")
tempDictionary.Add("TestName2", "TestValue2")
tempDictionary.Add("TestName3", "TestValue3")
'... and so on ...  '

For Each Item In tempDictionary
 DoStuff(Item)

 'Here is the point in which I want to see what the value of the'
 '开发者_Go百科dictionary item that is 1, 2, 3, [x] places in front of the current'
 'item without interfering w/ the current loop.'

 'CODE'

Next

Thanks in advance!


The problem is that a Dictionary is not an ordered data structure. You can't rely on the order on which items are enumerated in the Dictionary. If you need items to be in order you should stick to a generic List, or use a third-party ordered dictionary (for example Wintellect's Power Collections has one).

Actually, there is an OrderedDictionary in System.Collections.Specialized, but it's not generic.

If you want to create your own generic ordered dictionary, this CodeProject article might interest you.


The sounds like you want a c-style for loop. Forgive the C# here (since you asked in VB)

for (int i = 0; i < myArray.Length; i++) {
   object current = myArray[i];

   if(i + 1 < myArray.Length) { // make sure there is a next item
       object nextItem = myArray[i + 1]
   }
}

As mentioned, a Dictionary isn't ordered, but you could put the Keys in an array to use the above.


Here's a way to peek ahead. As for order, I won't repeat what others have already mentioned.

Dim peek As Integer = 2   ''// peek ahead amount
For i As Integer = 0 To tempDictionary.Count - 1
    Dim key = tempDictionary.Keys(i)
    Dim value = tempDictionary.Values(i)
    Console.WriteLine("{0} : {1}", key, value)

    Dim peekIndex As Integer = i + peek
    If peekIndex < tempDictionary.Count - 1 Then
        Dim nextKey = tempDictionary.Keys(peekIndex)
        Dim nextValue = tempDictionary.Values(peekIndex)
        Console.WriteLine("Peek: {0} : {1}", nextKey, nextValue)
    End If
Next


What you are asking is meaningless because the order in which the items are returned is undefined. There's no such thing as order in a dictionary.


I don't know what you want to do with the dictionary entry exactly but something like this should work:

Dim dict As New Dictionary(Of String, String)
For i As Integer = 0 To dict.Count - 2
  If dict.ElementAt(i).Equals(dict.ElementAt(i + 1)) Then Exit For
Next

ElementAt() is a Linq function. However, if you're iterating through the list like this I do wonder if the Dictionary is the thing to use. A Generic List may be better.


Most likely, you'd want to create an array with the keys from the dictionary in the order you want, and iterate through that using indexes.

That is, your for loop would be the vb equivalent of for (int i = 0; i < array.length; i++), and you'd look up dictionary values with dictionary[array[i]]. You could, of course, substitute i for i + 1 (with bounds checking).


The only ways I could think of are either creating a copy of the current enumerator and advancing that (which obviously gets quite ugly), or using the .skip() method from Linq to create an enumerable which works ahead and then combining both using some kind of combine method, which you would again have to define yourself.

Onto another point: In order to have a meaningful order of the elements you probably want to use a System.Generics.SortedDictionary instead of a standard one.

This method should work for combining two ienumerables given an adequate combiner method

    public static IEnumerable<TResult> Combine<TParam1, TParam2, TResult>(this IEnumerable<TParam1> self, IEnumerable<TParam2> other, 
                                                                              Func<TParam1, TParam2, TResult> combiner)
    {
        using(var first=self.GetEnumerator())
        using (var second = other.GetEnumerator())
        {
            while (first.MoveNext() && second.MoveNext())
                yield return combiner(first.Current, second.Current);
        }

    }

I can't gurantee this will work exactly like this since i had to strip out some code from the original snippet

Edit: After thinking about it a bit, this might be what you want: (unfortenatly in c# since my vb isn't al that great):

skippedEnumerable = myEnumerable;         
myEnumerable.Select(item => {skippedEnumerable = skippedEnumerable.Skip(1); return new KeyValuePair<T, IEnumerable<T>>(item, skippedEnumerable )});

This will give you a pair of the current item and an enumerable starting at the current position, so you can use .ElementAt() to easily offset into that enumerable. This might nest the enumerables pretty deep though, so it might (or might not due to the O(n^2) runtime) be better to use myEnumerable.Skipe(index) directly;

0

精彩评论

暂无评论...
验证码 换一张
取 消