Question

Je dois souvent trier un dictionnaire composé de clés et de valeurs, par valeur.Par exemple, j'ai un hachage de mots et de fréquences respectives, que je souhaite classer par fréquence.

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

Dictionnaire trié commandes par clé, pas par valeur.Certains ont recours à un classe personnalisée, mais existe-t-il un moyen plus propre ?

Était-ce utile?

La solution

Utiliser:

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

Puisque vous ciblez .NET 2.0 ou supérieur, vous pouvez simplifier cela dans la syntaxe lambda : c'est équivalent, mais plus court.Si vous ciblez .NET 2.0, vous ne pouvez utiliser cette syntaxe que si vous utilisez le compilateur de Visual Studio 2008 (ou version ultérieure).

var myList = aDictionary.ToList();

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

Autres conseils

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

Cela permettrait également une grande flexibilité dans la mesure où vous pouvez sélectionner les 10, 20, 10 %, etc.Ou si vous utilisez votre indice de fréquence des mots pour type-ahead, vous pouvez également inclure StartsWith clause également.

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

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

foreach (KeyValuePair<string,int> 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 manière Ruby de gérer les hachages.

Vous pouvez trier un dictionnaire par valeur et le sauvegarder dans lui-même (de sorte que lorsque vous le parcourez, 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 cela fonctionne.

À un niveau élevé, vous n'avez pas d'autre choix que de parcourir tout le dictionnaire et d'examiner chaque valeur.

Peut-être que ceci aide :http://bytes.com/forum/thread563638.htmlCopier/Coller 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);
    }
);

De toute façon, vous ne seriez jamais capable de trier un dictionnaire.Ils ne sont pas réellement commandés.Les garanties d'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'un ordre particulier.Par conséquent, vous devrez insérer la paire nom-valeur dans une liste.

Vous ne triez pas les entrées dans le dictionnaire.La classe de dictionnaire dans .NET est implémentée sous forme de table de hachage - 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'arbre de recherche binaire.

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

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

Si les objets de votre collection avaient une structure plus complexe (plus de champs) et que vous deviez pouvoir y accéder/trier efficacement en utilisant plusieurs champs différents comme clés - Vous auriez probablement besoin d'une structure de données personnalisée qui consisterait en le stockage principal qui prend en charge l'insertion et la suppression O(1) (LinkedList) et plusieurs structures d'indexation - Dictionaries/SortedDictionaries/SortedLists.Ces index utiliseraient l'un des champs de votre classe complexe comme clé et un pointeur/référence vers le LinkedListNode dans la LinkedList comme valeur.

Vous auriez besoin de coordonner les insertions et les suppressions pour garder vos index synchronisés avec la collection principale (LinkedList) et les suppressions seraient assez coûteuses, je pense.Ceci est similaire au fonctionnement des index de bases 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 envisagez d'effectuer un traitement de recherche intensif.Si vous n'avez besoin de les afficher qu'une fois triés par fréquence, vous pouvez simplement produire une liste de tuples (anonymes) :

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 pour vous amuser, vous pouvez utiliser une extension 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));

Trier les valeurs

Cela montre comment trier les valeurs dans un dictionnaire.Nous voyons un programme console que vous pouvez compiler dans Visual Studio et exécuter.Il ajoute des clés à un dictionnaire, puis les trie selon leurs valeurs.N'oubliez pas que les instances du dictionnaire ne sont initialement triées d'aucune façon.Nous utilisons le mot-clé LINQ orderby dans une instruction de requête.

Programme de clause OrderBy qui trie Dictionary [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;
    }
}

Sortir

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

Trier un SortedDictionary liste à lier dans un ListView contrôle à 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 :

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

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

//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 will contient la version triée de sections

Les autres réponses sont bonnes, si tout ce que vous voulez, c'est avoir une liste "temporaire" triée par valeur.Cependant, si vous souhaitez trier un dictionnaire par Key que synchronise automatiquement avec un autre dictionnaire trié par Value, vous pouvez utiliser le Bijection<K1, K2> classe.

Bijection<K1, K2> vous permet d'initialiser la collection avec deux dictionnaires existants, donc si vous voulez que l'un d'entre eux ne soit pas trié et que l'autre soit trié, vous pouvez créer votre bijection avec du code comme

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

Vous pouvez utiliser dict comme n'importe quel dictionnaire normal (il implémente IDictionary<K, V>), puis appelez dict.Inverse pour obtenir le dictionnaire "inverse" qui est trié par Value.

Bijection<K1, K2> fait partie de Loyc.Collections.dll, mais si vous le souhaitez, vous pouvez simplement copier le code source dans votre propre projet.

Note:S'il existe plusieurs clés avec la même valeur, vous ne pouvez pas utiliser Bijection, mais vous pouvez synchroniser manuellement entre un appareil ordinaire Dictionary<Key,Value> et un BMultiMap<Value,Key>.

Supposons que nous ayons un dictionnaire comme

   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) vous pouvez utiliser 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);
        }

En fait, en C #, les dictionnaires DINT ont des méthodes tri (), car vous êtes plus intéressé par la tri par les valeurs, vous ne pouvez pas obtenir de valeurs tant que vous ne les fournissez pas, en bref, vous devez les itérer, en utilisant l'ordre de Linq par,

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
}

tu peux faire un tour,

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

ou

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

cela dépend également du type de valeurs que vous stockez,
est-il unique (comme une chaîne, un int) ou multiple (comme une liste, un tableau, une classe définie par l'utilisateur),
s'il est unique, vous pouvez en faire une liste, puis appliquer le tri.
Si la classe définie par l'utilisateur, cette classe doit implémenter iCompparable,
ClassName: IComparable<ClassName> et remplacer compareTo(ClassName c)car ils sont plus rapides que LINQ et plus orientés objet.

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

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

Étant donné que vous disposez d'un dictionnaire, vous pouvez les trier directement sur les valeurs en utilisant ci-dessous une ligne :

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top