Comment trier un dictionnaire par valeur?

I often have to sort a dictionary, consisting of keys & values, by value. For example, I have a hash of words and respective frequencies, that I want to order by frequency.

Il y a un SortedList qui est bon pour une seule valeur (disons fréquence), que je veux mapper au mot.

SortedDictionary orders by key, not value. Some resort to a custom class, but is there a cleaner way?

0
ajouté édité
Vues: 4
En plus de trier le dictionnaire (comme dans la réponse acceptée), vous pouvez également créer un IComparer qui fait l'affaire (vrai qu'il accepte une clé à comparer, mais avec une clé, vous pouvez obtenir une valeur). ;-)
ajouté l'auteur BrainSlugs83, source

17 Réponses

À un niveau élevé, vous n'avez pas d'autre choix que de parcourir le Dictionnaire entier et de regarder chaque valeur.

Peut-être que cela aide: http://bytes.com/forum/thread563638.html Copier / Coller de John Timney:

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

List> myList = new List>(s);
myList.Sort(
    delegate(KeyValuePair firstPair,
    KeyValuePair nextPair)
    {
        return firstPair.Value.CompareTo(nextPair.Value);
    }
);
0
ajouté
stringnextPair -> chaîne de caractères> nextPair stringfirstPair -> chaîne de caractères> firstPair
ajouté l'auteur Art, source
Solution non-Linq parfaite. Cela ne cesse de m'étonner de voir comment les gens ressentent le besoin d'utiliser Linq même quand ce n'est absolument pas nécessaire pour résoudre le problème. Avec C# 3, je crois que vous pouvez aussi simplifier le tri pour utiliser simplement un lambda: myList.Sort ((x, y) => x.Value.CompareTo (y.Value));
ajouté l'auteur RobinHood70, source

En regardant autour, et en utilisant certaines fonctionnalités C# 3.0, nous pouvons le faire:

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

C'est la manière la plus propre que j'ai vue et elle est similaire à la façon ruby de manipuler les hachages.

0
ajouté
C'était pratique. Comment peut-il être inversé pour aller dans l'autre sens?
ajouté l'auteur Dan Hastings, source
@AndriusNaru? Evi? Ius: Si vous ajoutez les éléments résultants dans un dictionnaire, vous allez détruire la commande, car les dictionnaires ne sont pas garantis être commandé de façon particulière .
ajouté l'auteur O. R. Mapper, source
N'oubliez pas d'ajouter l'espace de noms System.Linq lorsque vous utilisez cette syntaxe.
ajouté l'auteur M. Dudley, source
(pour KeyValuePair élément dans keywordCounts.OrderBy (clé => key.Value) sélectionnez l'élément) .ToDictionary (t => t.Key, t => t.Value) - juste une petite addition à votre réponse :) Merci, btw :)
ajouté l'auteur Andrius Naruševičius, source
J'essayais de trier un dictionnaire en ajoutant les KeyValuePairs à un ComboBox ... cela a bien fonctionné! Merci!
ajouté l'auteur Jason Down, source

Utilisation:

using System.Linq.Enumerable;
...
List> myList = aDictionary.ToList();

myList.Sort(
    delegate(KeyValuePair pair1,
    KeyValuePair pair2)
    {
        return pair1.Value.CompareTo(pair2.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 Visual Studio 2008 (or above).

var myList = aDictionary.ToList();

myList.Sort((pair1,pair2) => pair1.Value.CompareTo(pair2.Value));
0
ajouté
c'est un paquebot - Vous n'avez pas besoin d'accolades. il peut être réécrit comme myList.Sort ((x, y) => x.Value.CompareTo (y.Value));
ajouté l'auteur Arnis Lapsa, source
Puis-je vous référer au Skeet stackoverflow.com/a/2705623/41211
ajouté l'auteur GONeale, source
désolé mais cette réponse était difficile à comprendre car je ne suis pas familier avec le mot-clé délégué (peut-être différent dans vb), n il n'est pas clair où le tri se passe comme vous auriez besoin d'exécuter le nombre d'éléments multiplié par le nombre d'éléments (non des éléments au carré) comparaisons en recherchant / comparant chaque élément au dictionnaire entier pour chaque élément, ou si vous faites simplement la comparaison entre le courant n dernier, alors vous auriez besoin de le faire en plus d'une boucle sur la collection, qui C'est pourquoi je n'ai pas «compris» tel quel. peu
ajouté l'auteur Erx_VB.NExT.Coder, source
Comment cette déclaration de délégué dans la méthode de tri apparaîtra-t-elle dans VB.NET?
ajouté l'auteur Jonas Axelsson, source
Vous êtes en train de compliquer cela - un dictionnaire implémente déjà IEnumerable , donc vous pouvez obtenir une liste triée comme ceci: var mySortedList = myDictionary.OrderBy (d => d.Value). ToList ();
ajouté l'auteur BrainSlugs83, source
J'ai utilisé cette solution (merci!) Mais j'ai été déroutée pendant une minute jusqu'à ce que je lis le message de Michael Stum (et son extrait de code de John Timney) et ai réalisé que myList est un objet secondaire, une liste de KeyValuePairs créée à partir du dictionnaire, puis triés.
ajouté l'auteur Robin Bennett, source
Je sais que c'est deux ans plus tard ... mais je suis sûr que cela pourrait aider quelqu'un à la place du commentaire précédent: myList.Sort (Fonction (firstPair As KeyValuePair (De Chaîne, Chaîne), nextPair As KeyValuePair (De Chaîne, Chaîne )) firstPair.Value.CompareTo (nextPair.Value))
ajouté l'auteur sacredfaith, source
Pour trier en descendant, changez le x et le y sur la comparaison: myList.Sort ((x, y) => y.Value.CompareTo (x.Value));
ajouté l'auteur Arturo, source
Je pense qu'il est intéressant de noter que cela nécessite Linq pour la méthode d'extension ToList.
ajouté l'auteur Ben, source

Utilisez LINQ:

Dictionary myDict = new Dictionary();
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;

Cela permettrait également une grande flexibilité dans le fait que vous pouvez sélectionner le top 10, 20 10%, etc. Ou si vous utilisez votre index de fréquence de mots pour type-ahead , vous pouvez également inclure StartsWith clause aussi bien.

0
ajouté
Ne tenez pas compte de toutes les suggestions de .ToDictionary - les dictionnaires standard ne garantissent pas un ordre de tri
ajouté l'auteur AlexFoxGill, source
Pour ceux qui utilisent .NET 2.0 - avez-vous essayé d'utiliser LINQBridge pour combler le vide? albahari.com/nutshell/linqbridge.aspx
ajouté l'auteur jocull, source
@BorisB. Je suis revenu à la version correcte. Tu aurais pu le faire aussi. N'importe qui peut éditer pour faire un meilleur endroit. Pour tous, ToDictionary n'est pas une bonne réponse à ce problème et la réponse originale de caryden ne l'a pas. Happy upvoting et un-downvoting.
ajouté l'auteur nawfal, source
Très bonne réponse. Et, comme mentionné dans d'autres commentaires sur d'autres réponses, assurez-vous d'inclure "using System.Linq;" en haut du fichier. Sinon, vous obtenez des messages d'erreur quelque peu confus et IntelliSense n'aide pas.
ajouté l'auteur Mark Meuer, source
Le type de retour doit être IEnumerable > ou un OrderedDictionary . Ou vous devriez utiliser un SortedDictionary dès le début. Pour un Dictionary simple, le MSDN indique clairement "L'ordre dans lequel les éléments sont renvoyés est indéfini.". Il semble que la dernière édition de @ rythos42 soit à blâmer. :)
ajouté l'auteur Boris B., source
Comment puis-je changer triedDict dans un Dictionary ? Nouvelle question SO Posted ici: stackoverflow.com/questions/3066182/…
ajouté l'auteur Kache, source
Malheureusement, cela ne fonctionne pas sur VS2005 en raison de. Net Framework 2.0 là (pas de LINQ). C'est bien d'avoir aussi la réponse de Bambrick.
ajouté l'auteur Smalcat, source
Je ne suis pas sûr que cela fonctionne toujours car l'itération sur le dictionnaire ne garantit pas que les KeyValuePairs sont "tirées" dans le même ordre que celui dans lequel elles ont été insérées. Ergo, cela n'a pas d'importance si vous utilisez orderby dans LINQ parce que Dictionary peut changer l'ordre des éléments insérés. Il fonctionne généralement comme prévu mais il n'y a AUCUNE GARANTIE, en particulier pour les grands dictionnaires.
ajouté l'auteur Bozydar Sobczak, source
J'ai juste utilisé une méthode simple pour chronométrer cela par rapport à l'autre réponse la mieux notée (en utilisant un Stopwatch() ), et je suis sorti avec une augmentation de presque 350% . Quatre (!) Liste de membres triés en utilisant une autre méthode = 0.0039511 secondes; (même) liste de quatre membres triés en utilisant la méthode LINQ = 0.0130195 secondes.
ajouté l'auteur mbrownnyc, source
var ordered = dict.OrderBy(x => x.Value);
0
ajouté
@theJerm: pas vrai
ajouté l'auteur AlexFoxGill, source
En utilisant le framework 4.5, vous venez de vérifier qu'il ne nécessite pas un cast vers le dictionnaire.
ajouté l'auteur Jagd, source
Je ne sais pas pourquoi cette solution n'est pas plus populaire - peut-être parce qu'elle nécessite .NET 3.5?
ajouté l'auteur Contango, source
Parce que cela nécessite un cast back to dictionary qui n'est pas toujours simple ...
ajouté l'auteur MoonKnight, source
@theJerm en remettant les items triés dans un dictionnaire, l'ordre est-il garanti? Cela pourrait fonctionner aujourd'hui, mais ce n'est pas garanti.
ajouté l'auteur nawfal, source
C'est une bonne solution, mais elle devrait l'avoir juste avant le point-virgule de fin: .ToDictionary (pair => pair.Key, pair => pair.Value);
ajouté l'auteur theJerm, source
Je préfère celui-ci, propre et simple. @Gravitas: Je suis d'accord, et la version du framework n'était pas mentionnée dans le PO.
ajouté l'auteur Andreas, source
Il ne devrait pas y avoir de renvoi à un dictionnaire car les dictionnaires ne sont pas commandés. Il n'y a aucune garantie que les KeyValuePairs resteront dans l'ordre que vous voulez.
ajouté l'auteur David DeMar, source

Vous ne seriez jamais capable de trier un dictionnaire de toute façon. Ils ne sont pas réellement commandés. Les garanties pour un dictionnaire sont que les collections de clés et de valeurs sont itérables, et que les valeurs peuvent être récupérées par index ou par clé, mais il n'y a aucune garantie d'ordre particulier. Par conséquent, vous auriez besoin d'obtenir la paire de valeur de nom dans une liste.

0
ajouté
@recursive Tout dictionnaire devrait le faire. Intéressant de noter que ma réponse, correcte, mais incomplète (aurait pu faire ce que les meilleurs exemples ont fait) est votée en dessous d'une réponse invalide qui entraînerait des exceptions sur les valeurs dupliquées dans le dictionnaire original (les clés sont uniques, les valeurs ne sont pas garanties être)
ajouté l'auteur Roger Willcocks, source
Un dictionnaire trié pourrait cependant donner une liste de paires clé-valeur.
ajouté l'auteur recursive, source
C'est la meilleure réponse, parce que le dictionnaire n'est pas triable. Il hash Keys et vous pouvez effectuer une opération de recherche extrêmement rapide sur elle.
ajouté l'auteur Paulius Zaliaduonis, source

Vous pouvez trier un dictionnaire par valeur et le sauvegarder sur lui-même (de sorte que lorsque vous le recouvrez, les valeurs apparaissent dans l'ordre):

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

Bien sûr, ce n'est peut-être pas correct, mais ça fonctionne.

0
ajouté
ajouté l'auteur AlexFoxGill, source
Je pense que cela fonctionnera, si des éléments sont ajoutés au dictionnaire en même temps et rien n'est enlevé / modifié. Y at-il des preuves que .NET réorganisera les éléments dans le dictionnaire?
ajouté l'auteur AaA, source
La sortie du dictionnaire n'est pas garantie d'avoir un ordre de tri particulier.
ajouté l'auteur Roger Willcocks, source
Ce "travail" n'est pas garanti. C'est un détail d'implémentation. Il n'a pas besoin de travailler d'autres fois. Mauvaise réponse, downvoted.
ajouté l'auteur nawfal, source
Je serais très inquiet de voir cela dans le code de production. Ce n'est pas garanti et pourrait changer à tout moment. Ce n'est pas que j'évite les solutions pragmatiques, cela montre juste un manque de compréhension de la structure de données.
ajouté l'auteur jamespconnor, source
Vous pouvez également utiliser OrderByDescending si vous souhaitez trier dans une liste descendante.
ajouté l'auteur Mendokusai, source
Travaillé pour moi, bien que je devais le changer légèrement à: Dictionnaire dict = dict.OrderBy (x => x.Value) .ToDictionary (x => x.Key, x => x.Value);
ajouté l'auteur Josh, source

Étant donné que vous avez un dictionnaire, vous pouvez les trier directement sur les valeurs en utilisant ci-dessous un doublure:

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
0
ajouté
ajouté l'auteur O. R. Mapper, source

Tri d'une liste SortedDictionary pour lier un contrôle ListView à l'aide de 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:


    
        
            
            
         
    

0
ajouté

The other answers are good, if you all you want is to have a "temporary" list sorted by Value. However, if you want to have a dictionary sorted by Key that automatically synchronizes with another dictionary that is sorted by Value, you could use the Bijection class.

Bijection allows you to initialize the collection with two existing dictionaries, so if you want one of them to be unsorted, and you want the other one to be sorted, you could create your bijection with code like

var dict = new Bijection(new Dictionary(), 
                               new SortedDictionary());

You can use dict like any normal dictionary (it implements IDictionary<>), and then call dict.Inverse to get the "inverse" dictionary which is sorted by Value.

Bijection is part of Loyc.Collections.dll, but if you want, you could simply copy the source code into your own project.

Note: In case there are multiple keys with the same value, you can't use Bijection, but you could manually synchronize between an ordinary Dictionary and a BMultiMap.

0
ajouté
Similaire à http://stackoverflow.com/questions/268321 mais peut remplacer chaque dictionnaire par SortedDictionary. Bien que les réponses semblent ne pas prendre en charge les valeurs en double (suppose 1 à 1).
ajouté l'auteur crokusek, source

Ou pour le plaisir, vous pouvez utiliser un peu d'extension LINQ:

var dictionary = new Dictionary { { "c", 3 }, { "a", 1 }, { "b", 2 } };
dictionary.OrderBy(x => x.Value)
  .ForEach(x => Console.WriteLine("{0}={1}", x.Key,x.Value));
0
ajouté

Trier les valeurs

Cela montre comment trier les valeurs dans un dictionnaire. Nous voyons un programme de console que vous pouvez compiler dans Visual Studio et exécuter. Il ajoute des clés à un dictionnaire, puis les trie selon leurs valeurs. Rappelez-vous que les instances de dictionnaire ne sont pas triées d'une manière ou d'une autre. Nous utilisons le mot-clé LINK orderby dans une requête.

Clause OrderBy Programme qui trie le dictionnaire [C #]

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

class Program
{
    static void Main()
    {
        // Example dictionary.
        var dictionary = new Dictionary(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 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;
    }
}

Sortie

dog: 0
cat: 1
programmer: 2
eel: 3
mouse: 5
0
ajouté

Vous ne triez pas les entrées dans le dictionnaire. La classe de dictionnaire dans .NET est implémentée sous forme de hashtable - cette structure de données n'est pas triable par définition.

Si vous devez pouvoir parcourir votre collection (par clé), vous devez utiliser SortedDictionary, qui est implémenté en tant qu'arborescence de recherche binaire.

Dans votre cas, cependant, la structure source n'est pas pertinente, car elle est triée par un champ différent. Vous devrez toujours trier par fréquence et le mettre dans une nouvelle collection triée par le champ pertinent (fréquence). Donc, dans cette collection, les fréquences sont des clés et les mots sont des valeurs. Comme de nombreux mots peuvent avoir la même fréquence (et que vous allez l'utiliser comme une clé), vous ne pouvez pas utiliser ni Dictionary ni SortedDictionary (ils nécessitent des clés uniques). Cela vous laisse avec une liste triée.

Je ne comprends pas pourquoi vous insistez pour maintenir un lien vers l'élément original dans votre dictionnaire principal / premier.

Si les objets de votre collection avaient une structure plus complexe (plus de champs) et que vous deviez être capable de les accéder / trier efficacement en utilisant plusieurs champs différents en tant que clés - Vous auriez probablement besoin d'une structure de données personnalisée constituée du stockage principal supporte O (1) insertion et suppression (LinkedList) et plusieurs structures d'indexation - Dictionnaires / SortedDictionaries / SortedLists. Ces index utiliseraient l'un des champs de votre classe complexe comme une clé et un pointeur / référence au LinkedListNode dans LinkedList comme une valeur.

Vous devez coordonner les insertions et les suppressions pour que vos index restent synchronisés avec la collection principale (LinkedList) et les suppressions seraient très coûteuses je pense. Ceci est similaire au fonctionnement des index de base de données - ils sont fantastiques pour les recherches, mais ils deviennent un fardeau lorsque vous devez effectuer de nombreuses insertions et suppressions.

Tout ce qui précède n'est justifié que si vous effectuez un traitement lourd de recherche. Si vous avez seulement besoin de les sortir une fois triés par fréquence alors vous pourriez simplement produire une liste de tuples (anonymes):

var dict = new SortedDictionary();
// 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);
}
0
ajouté

Le moyen le plus simple d'obtenir un dictionnaire trié est d'utiliser la classe SortedDictionary intégrée:

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

sortedSections will contains the sorted version of sections

0
ajouté
@mbrownnyc - nope, ce qui nécessite l'hypothèse ou la condition que les valeurs soient uniques, ce qui n'est pas garanti.
ajouté l'auteur Roger Willcocks, source
Comme vous le mentionnez dans votre commentaire, SortedDictionary trie par clé. L'OP veut trier par valeur. SortedDictionary n'aide pas dans ce cas.
ajouté l'auteur Marty Neal, source
Eh bien ... S'il / elle (vous) pouvez, définissez simplement les valeurs comme les clés. J'ai chronométré les opérations et sorteddictionary() a toujours triomphé d'au moins 1 microseconde, et il est beaucoup plus facile de le gérer (comme le fait de le convertir en quelque chose de facile à interagir avec un dictionnaire 0 (c'est déjà un sorteddictionary )).
ajouté l'auteur mbrownnyc, source

Vous pouvez trier le dictionnaire par valeur et obtenir le résultat dans le dictionnaire en utilisant le code ci-dessous:

Dictionary <> ShareUserNewCopy = 
       ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
                                                        pair => pair.Value);                                          
0
ajouté
Et pourquoi est-ce que vous ajoutez cette réponse quand cela est déjà répondu?
ajouté l'auteur nawfal, source
En remettant les éléments triés dans un dictionnaire, ils ne sont plus garantis d'être triés lorsque vous énumérez le nouveau dictionnaire.
ajouté l'auteur Marty Neal, source

Supposons que nous ayons un dictionnaire

   Dictionary dict = new Dictionary();
   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) vous pouvez utiliser le dictionnaire temporaire pour stocker les valeurs en tant que :

        Dictionary dctTemp = new Dictionary();

        foreach (KeyValuePair pair in dict.OrderBy(key => key.Value))
        {
            dctTemp .Add(pair.Key, pair.Value);
        }
0
ajouté
Dictionary dic= new Dictionary();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);
0
ajouté
Aussi incorrect. Voir ici: stackoverflow.com/a/4007787/463828
ajouté l'auteur Philipp M, source