Pergunta

Muitas vezes tenho que classificar um dicionário, composto por chaves e valores, por valor.Por exemplo, tenho um hash de palavras e respectivas frequências, que quero ordenar por frequência.

Existe um SortedList o que é bom para um único valor (por exemplo, frequência), que desejo mapeá-lo de volta à palavra.

Dicionário Classificado pedidos por chave, não por valor.Alguns recorrem a um classe personalizada, mas existe uma maneira mais limpa?

Foi útil?

Solução

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);
    }
);

Como seu objetivo é o .NET 2.0 ou superior, você pode simplificar isso na sintaxe lambda - é equivalente, mas mais curto.Se seu destino for o .NET 2.0, você só poderá usar essa sintaxe se estiver usando o compilador do Visual Studio 2008 (ou superior).

var myList = aDictionary.ToList();

myList.Sort((pair1,pair2) => pair1.Value.CompareTo(pair2.Value));

Outras dicas

Usar 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 permitiria grande flexibilidade, pois você pode selecionar os 10 primeiros, 20, 10%, etc.Ou se você estiver usando seu índice de frequência de palavras para type-ahead, você também pode incluir StartsWith cláusula também.

var ordered = dict.OrderBy(x => x.Value);

Olhando ao redor e usando alguns recursos do C# 3.0, 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 limpa que já vi e é semelhante à maneira Ruby de lidar com hashes.

Você pode classificar um Dicionário por valor e salvá-lo novamente (para que, quando você o examinar, os valores apareçam em ordem):

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

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

Em um nível superior, você não tem outra escolha a não ser percorrer todo o Dicionário e observar cada valor.

Talvez isso ajude:http://bytes.com/forum/thread563638.htmlCopiar/colar de 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);
    }
);

Você nunca seria capaz de classificar um dicionário de qualquer maneira.Na verdade, eles não são ordenados.As garantias para um dicionário são que as coleções de chaves e valores sejam iteráveis ​​e os valores possam ser recuperados por índice ou chave, mas não há garantia de qualquer ordem específica.Portanto, você precisaria colocar o par nome-valor em uma lista.

Você não classifica as entradas no Dicionário.A classe de dicionário em .NET é implementada como uma tabela hash - esta estrutura de dados não pode ser classificada por definição.

Se você precisar iterar sua coleção (por chave), precisará usar SortedDictionary, que é implementado como uma árvore de pesquisa binária.

No seu caso, porém, a estrutura de origem é irrelevante, pois está classificada por um campo diferente.Você ainda precisaria classificá-lo por frequência e colocá-lo em uma nova coleção classificada pelo campo relevante (frequência).Portanto, nesta coleção as frequências são chaves e as palavras são valores.Como muitas palavras podem ter a mesma frequência (e você vai usá-la como chave), você não pode usar nem Dictionary nem SortedDictionary (eles exigem chaves exclusivas).Isso deixa você com uma SortedList.

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

Se os objetos da sua coleção tivessem uma estrutura mais complexa (mais campos) e você precisasse ser capaz de acessá-los/classificá-los de forma eficiente usando vários campos diferentes como chaves - você provavelmente precisaria de uma estrutura de dados personalizada que consistiria no armazenamento principal que suporta inserção e remoção O(1) (LinkedList) e diversas estruturas de indexação - Dictionaries/SortedDictionaries/SortedLists.Esses índices usariam um dos campos da sua classe complexa como chave e um ponteiro/referência para LinkedListNode no LinkedList como valor.

Você precisaria coordenar inserções e remoções para manter seus índices sincronizados com a coleção principal (LinkedList) e as remoções seriam muito caras, eu acho.Isso é semelhante ao funcionamento dos índices de banco de dados - eles são fantásticos para pesquisas, mas se tornam um fardo quando você precisa realizar muitas inserções e exclusões.

Todos os itens acima só são justificados se você for fazer algum processamento pesado de pesquisa.Se você precisar exibi-los apenas uma vez classificados por frequência, poderá produzir apenas uma lista de tuplas (anônimas):

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);
}
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);

Ou, por diversão, você pode usar algumas vantagens da extensão 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));

Classificar valores

Isso mostra como classificar os valores em um Dicionário.Vemos um programa de console que você pode compilar no Visual Studio e executar.Ele adiciona chaves a um Dicionário e as classifica por seus valores.Lembre-se de que as instâncias do Dicionário não são inicialmente classificadas de forma alguma.Usamos a palavra-chave LINQ orderby em uma instrução de consulta.

Programa de cláusula de ordem que classifica o 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

Classificando um SortedDictionary lista para vincular em um ListView controle usando 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>

A maneira mais fácil de obter um dicionário classificado é usar o integrado SortedDictionary aula:

//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 irá conter a versão classificada de sections

As outras respostas são boas, se tudo o que você deseja é ter uma lista "temporária" ordenada por Valor.No entanto, se você quiser que um dicionário seja classificado por Key que sincroniza automaticamente com outro dicionário que é classificado por Value, você poderia usar o Bijection<K1, K2> aula.

Bijection<K1, K2> permite que você inicialize a coleção com dois dicionários existentes, então se você quiser que um deles não seja classificado e queira que o outro seja classificado, você pode criar sua bijeção com código como

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

Você pode usar dict como qualquer dicionário normal (ele implementa IDictionary<K, V>) e depois ligue dict.Inverse para obter o dicionário "inverso" que é classificado por Value.

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

Observação:Caso existam várias chaves com o mesmo valor, você não pode usar Bijection, mas você pode sincronizar manualmente entre um comum Dictionary<Key,Value> e um BMultiMap<Value,Key>.

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);
        }

Na verdade, em C#, os dicionários não têm métodos de classificação (), à medida que você está mais interessado em classificar por valores, você não pode obter valores até fornecer a chave para eles, em resumo, precisa iterar através deles, usando a ordem de Linq por,

var items = new Dictionary<string, int>();
items.Add("cat", 0);
items.Add("dog", 20);
items.Add("bear", 100);
items.Add("lion", 50);

// Call OrderBy method here on each item and provide them the ids.
foreach (var item in items.OrderBy(k => k.Key))
{
    Console.WriteLine(item);// items are in sorted order
}

você pode fazer um truque,

var sortedDictByOrder = items.OrderBy(v => v.Value);

ou

var sortedKeys = from pair in dictName
            orderby pair.Value ascending
            select pair;

também depende do tipo de valores que você está armazenando,
é único (como string, int) ou múltiplo (como List, Array, classe definida pelo usuário),
se for solteiro, você pode fazer uma lista e aplicar a classificação.
Se a classe definida pelo usuário, essa classe deve implementar o ICOMPARABLE,
ClassName: IComparable<ClassName> e substituir compareTo(ClassName c)pois são mais rápidos que o LINQ e mais orientados a objetos.

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);                                          

Dado que você tem um dicionário, você pode classificá-los diretamente nos valores usando um liner abaixo:

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top