tags:

views:

45924

answers:

12

I often have a Dictionary of keys & values and need to sort it by value. For example, I have a hash of words and their frequencies, and want to order them by frequency.

There's SortedList which is good for a single value (frequency), but I want to map it back to the word.

SortedDictionary orders by key, not value. Some resort to a custom class, but what's the cleanest way?

+11  A: 

Looking around, and using some C# 3.0 features we can do this:

foreach (KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value))
{
// do something with item.Key and item.Value
}

This is the cleanest way I've seen and is similar to the Ruby way of handling hashes.

kurious
I was trying to sort a dictionary while adding the KeyValuePairs to a ComboBox... this worked great! Thanks!
Jason Down
@Jason: You're welcome!
kurious
Don't forget to add the System.Linq namespace when using this syntax.
emddudley
+15  A: 

On a high level, you have no other choice then to walk through the whole Dictionary and look at each value.

Maybe this helps: http://bytes.com/forum/thread563638.html Copy/Pasting from John Timney:

Dictionary<string, string> s = new Dictionary<string, string>();
s.Add("1", "a Item");
s.Add("2", "c Item");
s.Add("3", "b Item");

List<KeyValuePair<string, string>> myList = new List<KeyValuePair<string, string>>(s);
myList.Sort(
    delegate(KeyValuePair<string, stringfirstPair>,
    KeyValuePair<string, stringnextPair>)
    {
        return firstPair.Value.CompareTo(nextPair.Value);
    }
);
Michael Stum
@Michael: Some of your <> characters have got butchered here
Chris Gill
Thanks, fixed (hopefully :))
Michael Stum
stringnextPair -> string> nextPairstringfirstPair -> string> firstPair
Art
+1  A: 

@Michael: Thanks for the reply! I did see that post, but was hoping to find a solution without creating an intermediate list.

I'm more familiar with Ruby than C#, which is like this

hash.sort{|a,b| a[1]<=>b[1]}

so I prefer the OrderBy method because it functions similarly to this. Thanks for the tip though.

kurious
+48  A: 
myList.Sort(
        delegate(KeyValuePair<string, string> firstPair,
        KeyValuePair<string, string> nextPair)
        {
            return firstPair.Value.CompareTo(nextPair.Value);
        }
    );

Since you're targeting .net 2.0 or above, you can simplify this into lambda syntax -- it's equivalent but shorter. If you're targeting .net 2.0 you can only use this syntax if you're using the compiler from vs2008 (or above).

myList.Sort((firstPair,nextPair) =>
    {
        return firstPair.Value.CompareTo(nextPair.Value);
    }
);
Leon Bambrick
I used this solution (Thanks!) but was confused for a minute until I read Michael Stum's post (and his code snippet from John Timney) and realised that myList is a secondary object, a list of KeyValuePairs, which is created from the dictionary, and then sorted.
Robin Bennett
sorry but this answer was difficult to understand as im not familiar with the delegate keyword (maybe different in vb), n it isn't clear where the sorting is happening as you'd either need to run number of item multiplied by number of items (no of items squared) comparisons by searching/comparing each element to the whole dictionary for each element, or if you are doing just comparing between current n last then you'd need to do that in more than one loop over the collection, which is why i didnt 'get it' as is. maybe more info on where the sorting n reordering is occuring woulda been helpful!
Erx_VB.NExT.Coder
it it's one liner - You don't need braces. it can be rewritten as `myList.Sort((x,y)=>x.Value.CompareTo(y.Value));`
Arnis L.
+2  A: 

@Leon: Thanks for the tip. This is for an internal app so we're using .NET 3.5. I like the lambda syntax as I'm not getting lost in the delegate/tag soup :).

kurious
+63  A: 

Why not use LINQ:

System.Collections.Generic.Dictionary<string, int> myDict = new Dictionary<string, int>();
    myDict.Add("one", 1);
    myDict.Add("four", 4);
    myDict.Add("two", 2);
    myDict.Add("three", 3);

    var sortedDict = (from entry in myDict orderby entry.Value ascending select entry);

This would also allow for great flexibility in that you can select the top 10, 20 10% etc. Or if you are using your word frequency index for type-ahead, you could also include StartsWith clause as well.

caryden
Thanks that really worked well for me.
Crash893
Great solution - best ive seen for this - and you dont have to create a list of keyvalue pairs before using it.. Very easy to understand :-)
schmoopy
spot on! Thanks
Jon
Thank you sir, this sorting approach came in handy for pulling top trends out of some Twitter data I'm molding :).
Dusda
How can I change sortedDict back into a Dictionary<string, int>? Posted new SO question here: http://stackoverflow.com/questions/3066182/c-convert-an-iorderedenumerablekeyvaluepairstring-int-into-a-dictionarystr
Kache4
Can I double 'up' this answer, please? Cleanest ever. Awesome
Narmatha Balasundaram
this really was the better answer, better than Leon Bambricks answer as that was difficult to understand with the information that was available i was not able to understand how to completely get it to work, but with this, i did. thanks.
Erx_VB.NExT.Coder
A: 

You'd never be able to sort a dictionary anyway. They are not actually ordered. The guarantees for a dictionary are that the key and value collections are iterable, and values can be retrieved by index or key, but here is no guarantee of any particular order. Hence you would need to get the name value pair into a list.

Roger Willcocks
A sorted dictionary could yield a list of key-value pairs though.
recursive
@recursive Any dictionary should yield that. Interesting to note that my answer, which is correct, but incomplete (could have done what the better examples did) is voted below an invalid answer that would result in exceptions on duplicate values in the original dictionary (keys are unique, values are not guaranteed to be)
Roger Willcocks
A: 

var sortedDict = (from entry in myDict orderby entry.Value ascending select entry);

in this case what can i use insted of var, and how can i reach first element with his value?

checho
A: 

The easiest way to get a sorted Dictionary is to use the built in SortedDictionary class:

        //Sorts sections according to the key value stored on "sections" unsorted dictionary, which is passed as a constructor argument
        System.Collections.Generic.SortedDictionary<int, string> sortedSections = null;
        if (sections != null)
        {
            System.Collections.Generic.sortedSections = new SortedDictionary<int, string>(sections);
        }

sortedSections will contains the sorted version of "sections"

Alex Ruiz
A: 

Sorting a SortedDictionary list to bind into a ListView control using VB.Net:

    Dim MyDictionary As SortedDictionary(Of String, MyDictionaryEntry)

        MyDictionaryListView.ItemsSource = MyDictionary.Values.OrderByDescending(Function(entry) entry.MyValue)

Public Class MyDictionaryEntry ' Need Property for GridViewColumn DisplayMemberBinding
    Public Property MyString As String
    Public Property MyValue As Integer
End Class

Xaml:

<ListView Name="MyDictionaryListView">
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Path=MyString}" Header="MyStringColumnName"></GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding Path=MyValue}" Header="MyValueColumnName"></GridViewColumn>
         </GridView>
    </ListView.View>
</ListView>
BSalita
A: 

Or for fun you could use some LINQ extension goodness:

var dictionary = new Dictionary<string, int> { { "c", 3 }, { "a", 1 }, { "b", 2 } };
dictionary.OrderBy(x => x.Value)
  .ForEach(x => Console.WriteLine("{0}={1}", x.Key,x.Value));
mythz
A: 

You can use the LINQ to sort the Dictionary by value.

Below link provide a good details about the same.

http://www.a2zmenu.com/CSharp/How-to-sort-CSharp-dictionary-by-value.aspx

rs.emenu