3

I have a dictionary:

Dim dicItems As Dictionary(of Integer, String)

The items in the dictionary are:

1,cat
2,dog
3,bird

I would like the order to be:

3,bird
2,dog
1,cat
Jim Wooley
  • 10,169
  • 1
  • 25
  • 43
user2221178
  • 95
  • 2
  • 6
  • 2
    A dictionary has no implicit order that you can rely on. – Tim Schmelter Mar 06 '14 at 15:17
  • @TimSchmelter: This is not true. Items in the dictionary are stored in the same order as they are added. Although the order does not matter or should not matter for any practical reason... – Victor Zakharov Mar 06 '14 at 15:40
  • 1
    @Neolisk: read the [docs](http://msdn.microsoft.com/en-us/library/xfhwa508.aspx): _"The order in which the items are returned is undefined"_ it is an implementation detail you cannot rely on that the order initially is the same as the insertion order. Actually the order will change as soon as the dictionary is modified. In better words (J. Skeet): http://stackoverflow.com/a/6384765/284240 – Tim Schmelter Mar 06 '14 at 15:43
  • @TimSchmelter: Can I sum this up as "if you delete an item from a dictionary, the order may unpredictably change?" In all other cases, the order will stay the same, i.e. add and update? – Victor Zakharov Mar 06 '14 at 15:48
  • 1
    @Neolisk: no, you haven't read the whole answer. It is more likely that the order stays the same but you should never rely your business logic on it. It may change under many circumstances(rehashing as Jon has mentioned or [>other reasons<](http://blogs.msdn.com/b/ericlippert/archive/2011/05/23/read-only-and-threadsafe-are-different.aspx)). Even if it seems to work always it might break in future versions of the framework. Dictionaries are simply not designed for that. Their purpose is to provide a fast lookup for the key. – Tim Schmelter Mar 06 '14 at 15:54

4 Answers4

3

You can't sort a dictionary, what you need is a sorted list instead.

Dim dicItems As New SortedList(Of Integer, String)

This will sort the items by the key value. If you want to get the items out in descending order like your example you could always do a loop starting from the end of the list, and moving to the beginning.

The below link has more information on SortedList's.

http://msdn.microsoft.com/en-us/library/ms132319%28v=vs.110%29.aspx

ShadowLiberal
  • 338
  • 2
  • 4
  • 13
2

You can use LINQ to solve this easily:

Dim dicItems As New Dictionary(Of Integer, String)
With dicItems
  .Add(1, "cat")
  .Add(2, "dog")
  .Add(3, "bird")
End With

dim query = from item in dicItems
            order by item.Key descending
            select item

If you want, you can also use the Lambda syntax:

Dim query = dicItems.OrderByDescending(Function(item) item.Key)
Jim Wooley
  • 10,169
  • 1
  • 25
  • 43
  • Thanks I was trying to use the extension .OrderByDescending method on my dictionary but I was not sure what to pass in for the parameters. dicItems.OrderByDescending. What you have here does work though – user2221178 Mar 07 '14 at 21:01
  • you should be able to do `dicItems.OrderByDescending(function(item) item.Key)` – Jim Wooley Mar 07 '14 at 21:10
  • I did try dicItems.OrderByDescending(Function(item) item.Key) but that did not seem to reverse the order. – user2221178 Mar 10 '14 at 15:02
  • I just tested it in LinqPad and the OrderByDescending method works. Double check your key value. Also, make sure you really need a dictionary in the first place if you are going to be accessing items by means other than their keys. – Jim Wooley Mar 10 '14 at 15:49
  • Ok I found the issue. I wasnt setting it to a new variable like you did. I just had dicItems.OrderByDescending(Function(item) item.Key) and then I was outputing the values to the console and the order did not reverse. – user2221178 Mar 10 '14 at 18:34
  • Correct. The LINQ extension methods don't alter the underlying data store. They just allow for fetching values in different ways (due to the new variable assignment). – Jim Wooley Mar 10 '14 at 19:12
2

A dictionary has no implicit order that you can rely on ("The order in which the items are returned is undefined").

As add-on for Shadows answer who suggest to use a SortedList you can get descending order by using the constructor that takes an IComparer(Of Int32):

Dim list = New SortedList(Of Integer, String)(New DescendingComparer())
list.Add(3, "bird")
list.Add(1, "cat")
list.Add(2, "dog")

Public Class DescendingComparer
    Implements IComparer(Of Int32)

    Public Function Compare(x As Integer, y As Integer) As Integer Implements System.Collections.Generic.IComparer(Of Integer).Compare
        Return y.CompareTo(x)
    End Function
End Class
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
0

Not sure why you would want to, since an order of items in the dictionary usually does not matter, but you can do it like this:

Dim dicItems As New Dictionary(Of Integer, String)
With dicItems
  .Add("1", "cat")
  .Add("2", "dog")
  .Add("3", "bird")
End With

Dim dicItemsReversed As New List(Of KeyValuePair(Of Integer, String))
dicItemsReversed.AddRange(dicItems.Reverse())

Notice that I output to a different collection, i.e. Generic.List in this case. If you want to replace your original contents, you can then do this:

dicItems.Clear()
For Each kv In dicItemsReversed
  dicItems.Add(kv.Key, kv.Value)
Next

As a variation on the topic, you can replace dicItems.Reverse() with other LINQ alternatives, such as OrderBy, so you can, for example, sort by Key, Value or a combination thereof. For example this dicItems.OrderBy(Function(x) x.Value) gives the output of:

3,bird    
1,cat
2,dog

(sorted alphabetically by value, ascending order)

Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151