[c#] Come si ordina un dizionario in base al valore?


7 Answers

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

Ciò consentirebbe anche una grande flessibilità in quanto è possibile selezionare i primi 10, 20 10%, ecc. Oppure se si utilizza l'indice di frequenza delle parole per la type-ahead , è possibile includere anche la clausola StartsWith .

Question

Spesso devo ordinare un dizionario, composto da chiavi e valori, in base al valore. Ad esempio, ho un hash di parole e rispettive frequenze, che voglio ordinare per frequenza.

C'è una SortedList che va bene per un singolo valore (diciamo la frequenza), che voglio ricondurre alla parola.

SortedDictionary ordini per chiave, non valore. Alcuni ricorrono a una lezione personalizzata , ma c'è un modo più pulito?




Ad un livello elevato, non hai altra scelta che percorrere l'intero dizionario e osservare ciascun valore.

Forse questo aiuta: http://bytes.com/forum/thread563638.html Copia / Incolla da 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);
    }
);



O per divertimento potresti usare qualche bontà di estensione 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));



Non si ordinano le voci nel dizionario. La classe dizionario in .NET è implementata come una tabella hash - questa struttura dati non è ordinabile per definizione.

Se è necessario essere in grado di eseguire iterazioni sulla raccolta (per chiave), è necessario utilizzare SortedDictionary, che viene implementato come albero di ricerca binario.

Nel tuo caso, tuttavia, la struttura di origine è irrilevante, perché è ordinata da un campo diverso. Dovresti comunque ordinarlo per frequenza e metterlo in una nuova raccolta ordinata per il campo pertinente (frequenza). Quindi in questa collezione le frequenze sono chiavi e le parole sono valori. Poiché molte parole possono avere la stessa frequenza (e lo si utilizzerà come chiave) non è possibile utilizzare né Dizionario né SortedDictionary (richiedono chiavi univoche). Questo ti lascia con una SortedList.

Non capisco perché insisti a mantenere un collegamento con l'articolo originale nel tuo dizionario principale / primo.

Se gli oggetti nella tua raccolta avevano una struttura più complessa (più campi) e dovevi essere in grado di accedervi / ordinarli efficientemente usando diversi campi come chiavi - Probabilmente avresti bisogno di una struttura dati personalizzata che sarebbe costituita dalla memoria principale che supporta O (1) inserimento e rimozione (LinkedList) e diverse strutture di indicizzazione - Dizionari / SortedDictionaries / SortedLists. Questi indici utilizzano uno dei campi della classe complessa come chiave e un puntatore / riferimento al LinkedListNode nella LinkedList come valore.

Dovresti coordinare gli inserimenti e le rimozioni per mantenere i tuoi indici sincronizzati con la raccolta principale (LinkedList) e le rimozioni sarebbero piuttosto costose. Questo è simile a come funzionano gli indici di database: sono fantastici per le ricerche ma diventano un fardello quando è necessario eseguire molte insezioni e cancellazioni.

Tutto quanto sopra è giustificato solo se hai intenzione di eseguire alcune operazioni di ricerca pesante. Se devi solo emetterli una volta ordinati per frequenza, puoi semplicemente creare un elenco di tuple (anonime):

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



Le altre risposte sono buone, se tutto quello che vuoi è avere una lista "temporanea" ordinata per Valore. Tuttavia, se si desidera disporre di un dizionario ordinato per Key che si sincronizza automaticamente con un altro dizionario ordinato per Value , è possibile utilizzare la Bijection<K1, K2> .

Bijection<K1, K2> ti permette di inizializzare la raccolta con due dizionari esistenti, quindi se vuoi che uno di essi sia non ordinato, e vuoi che l'altro sia ordinato, puoi creare la tua bijection con un codice come

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

È possibile utilizzare dict come qualsiasi dizionario normale (implementa IDictionary<> ), quindi chiamare dict.Inverse per ottenere il dizionario "inverso" ordinato per Value .

Bijection<K1, K2> è parte di Loyc.Collections.dll , ma se vuoi, puoi semplicemente copiare il codice sorgente nel tuo progetto.

Nota : nel caso in cui ci siano più chiavi con lo stesso valore, non è possibile utilizzare Bijection , ma è possibile sincronizzare manualmente tra un Dictionary<Key,Value> normale Dictionary<Key,Value> e un BMultiMap<Value,Key> .




Ordinamento di un elenco SortedDictionary da associare a un controllo ListView utilizzando 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>



Guardando intorno e usando alcune funzionalità del C # 3.0 possiamo fare questo:

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

Questo è il modo più pulito che ho visto ed è simile al modo in cui Ruby gestisce gli hash.




È possibile ordinare il dizionario in base al valore e ottenere il risultato nel dizionario utilizzando il seguente codice:

Dictionary <<string, string>> ShareUserNewCopy = 
       ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
                                                        pair => pair.Value);                                          



Related