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.
Há um SortedList
que é bom para um único valor (digamos frequência), que eu quero mapeá-lo de volta à palavra.
SortedDictionary ordena por chave, não por valor. Alguns recorrem a uma classe personalizada , mas existe uma maneira mais limpa?
c#
.net
sorting
dictionary
Kalid
fonte
fonte
IComparer
que faça o truque (é verdade que ele aceita uma chave para comparar, mas com uma chave, você pode obter um valor). ;-)Respostas:
Usar:
Como você está direcionando o .NET 2.0 ou superior, você pode simplificar isso na sintaxe lambda - é equivalente, mas menor. Se você estiver direcionando o .NET 2.0, poderá usar essa sintaxe apenas se estiver usando o compilador do Visual Studio 2008 (ou superior).
fonte
myList.Sort((x,y)=>x.Value.CompareTo(y.Value));
IEnumerable
, para que você possa obter uma lista ordenada como esta:var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
Use LINQ:
Isso também permitiria uma grande flexibilidade, pois você pode selecionar os 10, 20, 10% etc. etc. Ou, se estiver usando o índice de frequência de palavras
type-ahead
, também poderá incluir aStartsWith
cláusula.fonte
IEnumerable<KeyValuePair<TKey, TValue>>
ou umOrderedDictionary<TKey, TValue>
. Ou deve-se usar umSortedDictionary
desde o início. Para uma planície,Dictionary
o MSDN afirma claramente "A ordem na qual os itens são retornados é indefinida". Parece que a última edição de @ rythos42 é a culpada. :).ToDictionary
- dicionários padrão não garantem uma ordem de classificaçãofonte
Olhando em volta e usando alguns recursos do C # 3.0, podemos fazer isso:
Esta é a maneira mais limpa que eu já vi e é semelhante à maneira Ruby de lidar com hashes.
fonte
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)
- apenas uma pequena adição à sua resposta :) Obrigado, btw :)Você pode classificar um dicionário por valor e salvá-lo novamente (para que, quando for pesquisá-lo, os valores sejam ordenados):
Claro, pode não estar correto, mas funciona.
fonte
Em um nível alto, você não tem outra escolha a não ser percorrer todo o dicionário e analisar cada valor.
Talvez isso ajude: http://bytes.com/forum/thread563638.html Copiar / colar de John Timney:
fonte
Você nunca seria capaz de classificar um dicionário de qualquer maneira. Eles não são realmente encomendados. As garantias para um dicionário são de que a coleção de chaves e valores é iterável e os valores podem ser recuperados por índice ou chave, mas não há garantia de nenhuma ordem específica. Portanto, você precisaria inserir o par de valor do nome em uma lista.
fonte
Você não classifica entradas no dicionário. A classe de dicionário no .NET é implementada como uma hashtable - essa estrutura de dados não pode ser classificada por definição.
Se você precisar interagir com sua coleção (por chave) - precisará usar o SortedDictionary, que é implementado como uma Árvore de Pesquisa Binária.
No seu caso, no entanto, a estrutura de origem é irrelevante, porque é classificada por um campo diferente. Você ainda precisará 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ê a usará como chave), você não pode usar nem o Dictionary nem o 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 dicionário principal / primeiro.
Se os objetos em sua coleção possuírem uma estrutura mais complexa (mais campos) e você precisar acessá-los / classificá-los com eficiência usando vários campos diferentes como chaves - Você provavelmente precisará de uma estrutura de dados personalizada que consistir no armazenamento principal que suporta inserção e remoção de O (1) (LinkedList) e várias estruturas de indexação - Dictionaries / SortedDictionaries / SortedLists. Esses índices usariam um dos campos da sua classe complexa como uma chave e um ponteiro / referência para o LinkedListNode no LinkedList como um 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. É semelhante à maneira como os índices de banco de dados funcionam - eles são fantásticos para pesquisas, mas se tornam um fardo quando você precisa executar várias inserções e exclusões.
Todas as opções acima só são justificadas se você deseja fazer algum processamento pesado de consulta. Se você precisar produzi-los apenas uma vez classificados por frequência, poderá produzir uma lista de tuplas (anônimas):
fonte
fonte
Ou, por diversão, você pode usar alguns benefícios da extensão LINQ:
fonte
Classificando uma
SortedDictionary
lista para vincular a umListView
controle usando o VB.NET:XAML:
fonte
A maneira mais fácil de obter um dicionário classificado é usar a
SortedDictionary
classe incorporada :sortedSections
vontade contém a versão classificada desections
fonte
SortedDictionary
classifica por teclas. O OP quer classificar por valor.SortedDictionary
não ajuda neste caso.sorteddictionary()
sempre venci em pelo menos 1 microssegundo, e é muito mais fácil de gerenciar (como a sobrecarga de convertê-la novamente em algo facilmente interagida e gerenciada de maneira semelhante a um dicionário é 0 (já é umsorteddictionary
)).As outras respostas são boas, se tudo o que você deseja é ter uma lista "temporária" classificada por Valor. No entanto, se você quiser ter um dicionário classificado por
Key
que sincroniza automaticamente com outro dicionário que é classificada porValue
, você pode usar aBijection<K1, K2>
classe .Bijection<K1, K2>
permite que você inicialize a coleção com dois dicionários existentes; portanto, se você deseja que um deles não seja classificado e que o outro seja classificado, você pode criar sua bijeção com código comoVocê pode usar
dict
como qualquer dicionário normal (implementadoIDictionary<K, V>
) e depois chamardict.Inverse
para obter o dicionário "inverso" que é classificado porValue
.Bijection<K1, K2>
faz parte do Loyc.Collections.dll , mas se você quiser, poderá simplesmente copiar o código fonte em seu próprio projeto.Nota : Caso haja várias chaves com o mesmo valor, você não poderá usá-lo
Bijection
, mas poderá sincronizar manualmente entre um comumDictionary<Key,Value>
e umBMultiMap<Value,Key>
.fonte
Suponha que tenhamos um dicionário como
1) você pode usar
temporary dictionary to store values as
:fonte
Na verdade, em C #, os dicionários têm métodos sort (); como você está mais interessado em classificar por valores, não é possível obter valores até fornecer a chave, resumindo, você precisa iterá-los usando o Order By do LINQ,
você pode fazer um truque,
ou
também depende de que tipo de valores você está armazenando,
é único (como string, int) ou múltiplo (como List, Array, classe definida pelo usuário);
se único, você pode fazer uma lista e aplicar classificação.
se classe definida pelo usuário, essa classe deve implementar IComparable
ClassName: IComparable<ClassName>
e substituircompareTo(ClassName c)
, pois são mais rápidas que o LINQ e mais orientadas a objetos.fonte
Namespace necessário:
using System.Linq;
Ordenar por desc:
Ordem por Asc:
fonte
Você pode classificar o dicionário por valor e obter o resultado no dicionário usando o código abaixo:
fonte
Como você possui um dicionário, é possível classificá-los diretamente nos valores usando abaixo de um forro:
fonte