Como você classificar um dicionário por valor?

votos
671

Eu sempre tenho que classificar um dicionário, que consiste de chaves e valores, pelo valor. Por exemplo, eu tenho um hash de palavras e respectivas freqüências, que eu quero pedir por frequência.

Há um SortedListque é bom para um único valor (digamos freqüência), que eu quero mapeá-lo de volta para a palavra.

SortedDictionary ordens por chave, não o valor. Alguns recorrem a uma classe personalizada , mas há uma maneira mais limpa?

Publicado 02/08/2008 em 01:40
fonte usuário
Em outras línguas...                            


17 respostas

votos
473

Use LINQ:

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;

Isso também permite uma grande flexibilidade no que você pode selecionar o top 10, 20 a 10%, etc. Ou se você estiver usando o seu índice de frequência palavra type-ahead, você também poderia incluir StartsWithcláusula também.

Respondeu 04/08/2008 em 16:22
fonte usuário

votos
459

Usar:

using System.Linq.Enumerable;
...
List<KeyValuePair<string, string>> myList = aDictionary.ToList();

myList.Sort(
    delegate(KeyValuePair<string, string> pair1,
    KeyValuePair<string, string> pair2)
    {
        return pair1.Value.CompareTo(pair2.Value);
    }
);

Desde que você está alvejando .NET 2.0 ou acima, você pode simplificar isso em sintaxe lambda - é equivalente, mas mais curto. Se você está alvejando .NET 2.0 você só pode usar esta sintaxe se você estiver usando o compilador do Visual Studio 2008 (ou superior).

var myList = aDictionary.ToList();

myList.Sort((pair1,pair2) => pair1.Value.CompareTo(pair2.Value));
Respondeu 02/08/2008 em 02:15
fonte usuário

votos
179
var ordered = dict.OrderBy(x => x.Value);
Respondeu 11/11/2010 em 18:16
fonte usuário

votos
148

Olhando em volta, e utilizando alguns C # 3.0 recursos que podemos fazer isso:

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

Esta é a maneira mais limpas que eu já vi e é semelhante à maneira como o Ruby de lidar com hashes.

Respondeu 02/08/2008 em 01:43
fonte usuário

votos
140

Você pode classificar um dicionário pelo valor e salvá-lo de volta para si mesmo (para que quando você foreach sobre ele os valores saem em ordem):

dict = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);

Claro, ele pode não estar correto, mas funciona.

Respondeu 22/06/2011 em 11:26
fonte usuário

votos
56

Em um nível alto, você não tem outra escolha, em seguida, para percorrer todo o dicionário e olhar para cada valor.

Talvez isso ajude: http://bytes.com/forum/thread563638.html copiar / colar a partir 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, string> firstPair,
    KeyValuePair<string, string> nextPair)
    {
        return firstPair.Value.CompareTo(nextPair.Value);
    }
);
Respondeu 02/08/2008 em 01:47
fonte usuário

votos
22

Você nunca seria capaz de classificar um dicionário de qualquer maneira. Eles não são realmente ordenou. As garantias para um dicionário é que as coleções de chave e valor são iterable, e os valores podem ser recuperados por índice ou chave, mas aqui não é garantia de qualquer ordem particular. Daí você precisa para obter o par de valores nome em uma lista.

Respondeu 19/12/2008 em 23:47
fonte usuário

votos
16

Você não classificar as entradas no dicionário. classe Dictionary em .NET é implementado como um hashtable - esta estrutura de dados não é classificável por definição.

Se você precisa ser capaz de interagir sobre sua coleção (por chave) - você precisa usar SortedDictionary, que é implementado como um binário Pesquisa Tree.

No seu caso, no entanto, a estrutura de origem é irrelevante, porque é classificado por um campo diferente. Você ainda precisa resolver isso por freqüência e colocá-lo em uma nova coleção classificada pelo campo relevante (frequência). Assim, nesta coleção as frequências são as chaves e as palavras são valores. Uma vez que muitas palavras podem ter a mesma frequência (e você está indo para usá-lo como uma chave), você não pode usar nenhum dicionário, nem SortedDictionary (eles exigem chaves únicas). Isso deixa você com um SortedList.

Eu não entendo por que insiste em manter um link para o item original em seu principal primeiro dicionário /.

Se os objetos em sua coleção tinha uma estrutura mais complexa (mais campos) e você necessário para ser capaz de forma eficiente de acesso / classificá-los usando vários campos diferentes como chaves - Você provavelmente precisará de uma estrutura de dados personalizado que consistiria no armazenamento principal que suporta O (1) de inserção e de remoção (ListaLigada) e várias estruturas de indexação - dicionários / SortedDictionaries / SortedLists. Estes índices usaria um dos campos de sua classe complexo como uma chave e um ponteiro / referência ao LinkedListNode no LinkedList como um valor.

Você precisaria de coordenar inserções e remoções para manter seus índices em sincronia com a coleção principal (LinkedList) e remoções seria muito caro eu acho. Isso é semelhante a como os índices de banco de dados trabalhar - eles são fantásticos para pesquisas, mas eles se tornam um fardo quando você precisa realizar muitas insetions e exclusões.

Todos os itens acima é justificada apenas se você estiver indo para fazer algum processamento pesado look-up. Se você só precisa enviá-las uma vez ordenadas por frequência, então você poderia apenas produzir uma lista de tuplas (anónimo):

var dict = new SortedDictionary<string, int>();
// ToDo: populate dict

var output = dict.OrderBy(e => e.Value).Select(e => new {frequency = e.Value, word = e.Key}).ToList();

foreach (var entry in output)
{
    Console.WriteLine("frequency:{0}, word: {1}",entry.frequency,entry.word);
}
Respondeu 13/12/2012 em 07:19
fonte usuário

votos
12
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);
Respondeu 20/07/2015 em 11:01
fonte usuário

votos
10

valores de ordenação

Isso mostra como classificar os valores em um dicionário. Vemos um programa de console você pode compilar no Visual Studio e correr. Ele adiciona chaves de um dicionário e, em seguida, classifica-as por seus valores. Lembre-se que os casos dicionário não são inicialmente classificadas de forma alguma. Nós usamos a palavra-chave orderby LINQ em uma instrução de consulta.

Programa cláusula OrderBy que classifica dicionário [C #]

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // Example dictionary.
        var dictionary = new Dictionary<string, int>(5);
        dictionary.Add("cat", 1);
        dictionary.Add("dog", 0);
        dictionary.Add("mouse", 5);
        dictionary.Add("eel", 3);
        dictionary.Add("programmer", 2);

        // Order by values.
        // ... Use LINQ to specify sorting by value.
        var items = from pair in dictionary
                orderby pair.Value ascending
                select pair;

        // Display results.
        foreach (KeyValuePair<string, int> pair in items)
        {
            Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
        }

        // Reverse sort.
        // ... Can be looped over in the same way as above.
        items = from pair in dictionary
        orderby pair.Value descending
        select pair;
    }
}

Saída

dog: 0
cat: 1
programmer: 2
eel: 3
mouse: 5
Respondeu 20/07/2012 em 10:49
fonte usuário

votos
10

Ou para se divertir você poderia usar alguma extensão bondade LINQ:

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));
Respondeu 30/06/2010 em 12:12
fonte usuário

votos
9

Classificar uma SortedDictionarylista para se ligar a um ListViewcontrolo utilizando 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>
Respondeu 23/04/2010 em 10:36
fonte usuário

votos
5

As outras respostas são bons, se você tudo que você quer é ter uma lista de "temporário" ordenados por valor. No entanto, se você quiser ter um dicionário classificado por Keyque sincroniza automaticamente com outro dicionário que é classificada por Value, você pode usar a Bijection<K1, K2>classe .

Bijection<K1, K2> permite inicializar a coleção com dois dicionários existentes, então se você quiser um deles para ser indiferenciados, e você quer o outro de ser resolvido, você pode criar o seu bijection com um código como

var dict = new Bijection<Key, Value>(new Dictionary<Key,Value>(), 
                               new SortedDictionary<Value,Key>());

Você pode usar dictcomo qualquer dicionário normal (ele implementa IDictionary<>), e em seguida, chamar dict.Inversepara obter o dicionário "inversa", que é classificada por Value.

Bijection<K1, K2>é parte de Loyc.Collections.dll , mas se você quiser, você pode simplesmente copiar o código-fonte em seu próprio projeto.

Nota : No caso de existirem várias chaves com o mesmo valor, você não pode usar Bijection, mas você pode sincronizar manualmente entre uma ordinária Dictionary<Key,Value>e uma BMultiMap<Value,Key>.

Respondeu 26/02/2016 em 07:15
fonte usuário

votos
5

A maneira mais fácil de obter um dicionário ordenado é usar o construída em SortedDictionaryclasse:

//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)
{
    sortedSections = new SortedDictionary<int, string>(sections);
}

sortedSections vontade contém a versão classificada de sections

Respondeu 02/04/2010 em 23:36
fonte usuário

votos
4

Suponha que temos um dicionário como

   Dictionary<int, int> dict = new Dictionary<int, int>();
   dict.Add(21,1041);
   dict.Add(213, 1021);
   dict.Add(45, 1081);
   dict.Add(54, 1091);
   dict.Add(3425, 1061);
   sict.Add(768, 1011);

1) você pode usar temporary dictionary to store values as:

        Dictionary<int, int> dctTemp = new Dictionary<int, int>();

        foreach (KeyValuePair<int, int> pair in dict.OrderBy(key => key.Value))
        {
            dctTemp .Add(pair.Key, pair.Value);
        }
Respondeu 02/02/2015 em 10:46
fonte usuário

votos
-2

Dado que você tem um dicionário que você pode classificá-los diretamente sobre valores usando abaixo um forro:

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
Respondeu 31/05/2014 em 23:30
fonte usuário

votos
-2

Você pode classificar o dicionário por valor e obter o resultado no dicionário usando o código abaixo:

Dictionary <<string, string>> ShareUserNewCopy = 
       ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
                                                        pair => pair.Value);                                          
Respondeu 24/07/2012 em 13:24
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more