Bir sözlüğü değere göre nasıl sıralarsınız?


796

Sık sık anahtar ve değerlerden oluşan bir sözlüğü değere göre sıralamak zorundayım. Örneğin, frekansa göre sipariş etmek istediğim bir kelime ve ilgili frekans karmalarım var.

Bir yoktur SortedListgeri kelimenin eşlemek istediğiniz, tek bir değer (diyelim frekans) için iyidir.

Sıralama değerlerini değil, anahtarla sıralama. Bazıları özel bir sınıfa başvurur , ancak daha temiz bir yol var mı?


1
Sadece sözlük sıralamak dışında (kabul edilen cevapta olduğu gibi), sadece IComparerhile yapan bir tane oluşturabilirsiniz (karşılaştırmak için bir anahtarı kabul ettiği doğrudur, ancak bir anahtarla bir değer elde edebilirsiniz). ;-)
BrainSlugs83

Yanıtlar:


520

kullanın:

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

.NET 2.0 veya üstünü hedeflediğiniz için bunu lambda sözdizimine sadeleştirebilirsiniz - eşdeğer, ancak daha kısa. .NET 2.0'ı hedefliyorsanız, bu sözdizimini yalnızca Visual Studio 2008 (veya üstü) derleyicisini kullanıyorsanız kullanabilirsiniz.

var myList = aDictionary.ToList();

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

26
Bu çözümü kullandım (Teşekkürler!) Ama Michael Stum'un gönderisini (ve John Timney'den kod snippet'ini) okuyana kadar bir dakika karıştı ve myList'in ikincil bir nesne, sözlükten oluşturulan KeyValuePairs'in bir listesi olduğunu fark ettim, ve sonra sıralanır.
Robin Bennett

113
bu bir astar - parantez gerekmez. yeniden yazılabilirmyList.Sort((x,y)=>x.Value.CompareTo(y.Value));
Arnis Lapsa

25
Azalan sıralama için karşılaştırmada x ve y değerlerini değiştirin: myList.Sort ((x, y) => y.Value.CompareTo (x.Value));
Arturo

8
Bunun ToList uzantısı yöntemi için Linq gerektirdiğini belirtmek gerekir.
Ben

17
Siz bunu karmaşık hale getirmek için waaaay vardır - bir sözlük zaten uygular IEnumerable, böylece böyle bir sıralama listesi alabilirsiniz:var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
BrainSlugs83

523

LINQ kullanın:

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;

Bu aynı zamanda en yüksek 10, 20% 10, vs.'yi seçebilmeniz için büyük esneklik sağlar. Veya kelime sıklığı dizininizi kullanıyorsanız, yan tümcesi type-aheadde ekleyebilirsiniz StartsWith.


15
SortDict'i tekrar Dictionary <string, int> olarak nasıl değiştirebilirim? Buraya yeni SO sorusu gönderildi: stackoverflow.com/questions/3066182/…
Kache

1
Ne yazık ki bu .net framework 2.0 orada (LINQ) nedeniyle VS2005 üzerinde çalışmaz. Bambrick'in cevabına sahip olmak da iyidir.
Smalcat

22
Her zaman işe yarayıp yaramadığından emin değilim çünkü sözlük üzerinden yineleme KeyValuePairs'in eklendikleri sırayla "çekildiğini" garanti etmez. Ergo, LINQ'da orderby kullanmanız önemli değildir, çünkü Sözlük eklenen öğelerin sırasını değiştirebilir. Genellikle beklendiği gibi çalışır, ancak özellikle büyük sözlükler için GARANTİ YOKTUR.
Bozydar Sobczak

16
Dönüş türü IEnumerable<KeyValuePair<TKey, TValue>>veya olmalıdır OrderedDictionary<TKey, TValue>. Ya SortedDictionaryda başlangıçtan itibaren a kullanmalısınız . Bir düzlükte DictionaryMSDN açıkça "Öğelerin döndürülme sırası tanımsızdır" ifadesini belirtir. @ Rythos42'nin son düzenlemesi suçlanıyor gibi görünüyor. :)
Boris B.7

16
Lütfen tüm önerileri dikkate almayın .ToDictionary- standart sözlükler sıralama düzenini garanti etmez
AlexFoxGill

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

6
Bu çözüm neden daha popüler değil emin değilim - belki de .NET 3.5 gerektirdiği için?
Contango

33
Bu iyi bir çözümdür, ancak noktalı virgül sonlanmadan önce bu hakka sahip olmalıdır: .ToDictionary (pair => pair.Key, pair => pair.Value);
Jerm

3
@TheJerm sıralanan öğeleri tekrar bir sözlüğe koyarak sipariş garanti sonra garanti? Bugün işe yarayabilir, ancak garanti edilmez.
nawfal

1
4.5 çerçevesini kullanarak, sadece doğrulamıştır değil sözlüğe bir döküm geri gerektirir.
Jagd

12
Sözlükler sıralanmadığından, sözlüğe geri verilmemelidir. KeyValuePairs'in istediğiniz sırada kalacağının garantisi yoktur.
David DeMar

165

Etrafa bakın ve bazı C # 3.0 özelliklerini kullanarak bunu yapabiliriz:

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

Bu gördüğüm en temiz yoldur ve Ruby işlemlerini Ruby yöntemine benzer.


Bir ComboBox KeyValuePairs eklerken bir sözlük sıralamak çalışıyordu ... Bu harika çalıştı! Teşekkürler!
Jason Down

6
Bu sözdizimini kullanırken System.Linq ad alanını eklemeyi unutmayın.
M Dudley

4
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)- Cevabınıza küçük bir katkı :) Teşekkürler, btw :)
Andrius Naruševičius

7
@ AndriusNaruševičius: Ortaya çıkan öğeleri tekrar bir sözlüğe eklerseniz, sözlüklerin belirli bir şekilde sipariş edileceği garanti edilmediğinden siparişi yok edersiniz .
VEYA Haritacı

Bu kullanışlıydı. Diğer yöne gitmek nasıl tersine çevrilebilir?
Dan Hastings

158

Bir Sözlüğü değere göre sıralayabilir ve kendisine geri kaydedebilirsiniz (böylece, üzerinde öngörüldüğünüzde değerler sırayla gelir):

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

Tabii, doğru olmayabilir, ama işe yarıyor.


8
Azalan bir listeye sıralamak istiyorsanız OrderByDescending komutunu da kullanabilirsiniz.
Mendokusai

Benim için çalıştı, ancak biraz değiştirmek zorunda kaldım: Sözlük <anahtar, değer> dict = dict.OrderBy (x => x.Value) .ToDictionary (x => x.Key, x => x.Value);
Josh

17
Bu "çalışma" garanti edilmez. Bu bir uygulama detayı. Başka zamanlarda çalışmasına gerek yok. Yanlış cevap, reddedildi.
nawfal

4
Sözlük çıktısının belirli bir sıralama düzenine sahip olduğu garanti EDİLMEZ.
Roger Willcocks

5
Bunu üretim kodunda görmek beni oldukça endişelendiriyor. Garanti edilmez ve herhangi bir zamanda değişebilir. Pragmatik çözümlerden kaçındığım için değil, sadece veri yapısı imo'yu anlamada bir eksiklik gösteriyor.
jamespconnor

61

Yüksek düzeyde, tüm Sözlüğü dolaşmak ve her bir değere bakmaktan başka seçeneğiniz yoktur.

Belki bu yardımcı olur: http://bytes.com/forum/thread563638.html John Timney'den Kopyalama / Yapıştırma:

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

3
stringnextPair -> string> nextPair stringfirstPair -> string> firstPair
Sanat

2
Mükemmel Linq olmayan çözüm. Hiçbir zaman insanların problemi çözmek için kesinlikle gerekli olmasa bile Linq kullanma gereksinimini nasıl hissettiklerini şaşırtmaktan vazgeçmez. C # 3 ile, sadece lambda kullanmak için Sıralamayı basitleştirebileceğinizi düşünüyorum: myList.Sort ((x, y) => x.Value.CompareTo (y.Value));

25

Bir sözlüğü asla asla sıralayamazsınız. Aslında sıralı değiller. Bir sözlüğün garantileri, anahtar ve değer koleksiyonlarının yinelenebilir olması ve değerlerin dizin veya anahtarla alınabilmesidir, ancak belirli bir siparişin garantisi yoktur. Bu nedenle, ad değeri çiftini bir listeye almanız gerekir.


2
Sıralı sözlük yine de anahtar / değer çiftlerinin bir listesini verebilir.
özyinelemeli

1
@recursive Herhangi bir sözlük bunu vermelidir. Cevabımın doğru, ancak eksik (daha iyi örneklerin yaptıklarını yapmış olabilir), orijinal sözlükteki yinelenen değerlerde istisnalara neden olacak geçersiz bir cevabın altında oylandığını belirtmek ilginç (anahtarlar benzersiz, değerler garanti edilmez olmak)
Roger Willcocks

5
Bu bes yanıttır, çünkü Sözlük sıralanabilir değildir. Tuşları hash eder ve üzerinde son derece hızlı bir arama işlemi gerçekleştirebilirsiniz.
Paulius Zaliaduonis

@NetMage Evet. Ama sorunun diğer kısmı Value tarafından sipariş edilmelerini istediler. Ve bunu ancak Anahtar ve Değer değiştirerek yapabilirsiniz. Ve Değer mutlaka benzersiz değildir, ancak Anahtar olmalıdır.
Roger Willcocks

Evet, ancak cevabınızdaki mutlak ifadeler nedeniyle cevabınızın yanlış olduğunu düşünüyorum.
NetMage

21

Sözlükteki girişleri sıralamazsınız. .NET'teki sözlük sınıfı karma olarak uygulanır - bu veri yapısı tanım gereği sıralanabilir değildir.

Koleksiyonunuz üzerinde yineleme yapabilmeniz gerekiyorsa (anahtar ile) - İkili Arama Ağacı olarak uygulanan SortedDictionary'yı kullanmanız gerekir.

Ancak sizin durumunuzda, kaynak yapı önemsizdir, çünkü farklı bir alana göre sıralanmıştır. Yine de frekansa göre sıralamanız ve ilgili alana (frekans) göre sıralanmış yeni bir koleksiyona koymanız gerekir. Yani bu koleksiyonda frekanslar anahtarlar ve kelimeler değerlerdir. Birçok kelime aynı frekansa sahip olabileceğinden (ve bunu anahtar olarak kullanacağınızdan) ne Dictionary ne de SortedDictionary kullanamazsınız (benzersiz anahtarlar gerektirir). Bu size bir SortedList bırakır.

Ana / ilk sözlüğünüzdeki orijinal öğeye bir bağlantıyı sürdürmek konusunda neden ısrar ettiğinizi anlamıyorum.

Koleksiyonunuzdaki nesneler daha karmaşık bir yapıya sahipse (daha fazla alan) ve anahtar olarak birkaç farklı alanı kullanarak bunlara verimli bir şekilde erişebilmeniz / bunları sıralayabilmeniz gerekiyorsa - Büyük olasılıkla, O (1) ekleme ve kaldırma (LinkedList) ve çeşitli indeksleme yapılarını destekler - Sözlükler / SortedDictionaries / SortedLists. Bu dizinler, bir anahtar olarak karmaşık sınıfınızdaki alanlardan birini ve bir değer olarak LinkedList'teki LinkedListNode öğesine bir işaretçi / başvuru kullanır.

Ana koleksiyon (LinkedList) ile senkronize tutmak için eklemeler ve kaldırma koordine gerekir ve kaldırma sanırım oldukça pahalı olurdu. Bu, veritabanı dizinlerinin çalışma şekline benzer - aramalar için harikalar, ancak birçok insetion ve silme işlemi yapmanız gerektiğinde bir yük haline gelirler.

Yukarıdakilerin tümü, ancak bazı ağır arama işlemleri yapacaksanız haklıdır. Bunları yalnızca frekansa göre sıralandığında bir kez çıkarmanız gerekiyorsa, (anonim) grupların bir listesini üretebilirsiniz:

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

15
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);

12

Veya eğlenmek için bazı LINQ uzantısı iyiliği kullanabilirsiniz:

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

10

VB.NET kullanarak denetime SortedDictionarybağlanmak için bir listeyi sıralama ListView:

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>

7

Sıralı bir Sözlük almanın en kolay yolu yerleşik SortedDictionarysınıfı kullanmaktır:

//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 , sıralı sürümünü içerir sections


7
Yorumunuzda belirttiğiniz gibi, SortedDictionarytuşlara göre sıralar. OP değere göre sıralamak istiyor. SortedDictionarybu durumda yardımcı olmaz.
Marty Neal

Şey ... Eğer yapabilirse, sadece değerleri anahtar olarak ayarlayın. Operasyonları zamanladım ve sorteddictionary()her zaman en az 1 mikrosaniye kadar kazandım ve yönetilmesi çok daha kolay (çünkü kolayca Sözlük ile kolayca etkileşime girebilen ve bir Sözlük'e benzer şekilde yönetilen bir şey haline getirme yükü 0 (zaten bir sorteddictionary)).
mbrownnyc

2
@ mbrownnyc - hayır, bunu yapmak DEĞERLERİN benzersiz olduğu varsayımını veya önkoşulunu gerektirir, ki bu garanti edilmez.
Roger Willcocks

6

Diğer yanıtlar iyidir, eğer istediğiniz tek şey Değer'e göre sıralanmış "geçici" bir listeye sahip olmaksa. Eğer sıralama kriteri bir sözlük yapmak istiyorsanız Ancak, Keybu otomatik olarak senkronize göre sıralandı başka sözlükle Valuesen kullanabilirsiniz, Bijection<K1, K2>sınıf .

Bijection<K1, K2> koleksiyonun mevcut iki sözlükle başlatılmasını sağlar, bu nedenle bunlardan birinin ayrılmasını istemiyorsanız ve diğerinin sıralanmasını istiyorsanız, kodunuzu aşağıdaki gibi kullanarak oluşturabilirsiniz:

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

dictHerhangi bir normal sözlük gibi kullanır (uygular IDictionary<K, V>) ve sonra dict.Inversesıralanan "ters" sözlüğü almak için arayın Value.

Bijection<K1, K2>Loyc.Collections.dll'nin bir parçasıdır , ancak isterseniz kaynak kodunu kendi projenize kopyalayabilirsiniz .

Not : Aynı değere sahip birden fazla anahtar olması durumunda, kullanamazsınız Bijection, ancak normal Dictionary<Key,Value>ve a arasında manuel olarak senkronizasyon yapabilirsiniz BMultiMap<Value,Key>.


Benzer http://stackoverflow.com/questions/268321 ama SortedDictionary her Sözlük yerini alabilir. Her ne kadar cevaplar yinelenen değerleri desteklemese de (1'den 1'e varsayılmaktadır).
crokusek

3

Diyelim ki bir sözlük var

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

3

Aslında C # 'da, Sözlükler dint sort () yöntemlerine sahiptir, değerlere göre sıralamakla daha fazla ilgileniyorsanız, onlara anahtar verene kadar değer alamazsınız, kısacası, LINQ's Order By,

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
}

bir numara yapabilirsin,

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

veya

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

aynı zamanda ne tür değerleri sakladığınıza bağlıdır,
tek (dize, int gibi) veya çoklu (Liste, Dizi, kullanıcı tanımlı sınıf gibi),
eğer teklerse listeyi yapabilir ve sıralama uygulayabilirsiniz.
kullanıcı tanımlı sınıf varsa, o sınıf IComparable'ı uygulamalı
ClassName: IComparable<ClassName>ve LINQ'dan compareTo(ClassName c) daha hızlı ve daha nesne yönelimli olduklarından geçersiz kılmalıdır .


0

Gerekli ad alanı: using System.Linq;

Dictionary<string, int> counts = new Dictionary<string, int>();
counts.Add("one", 1);
counts.Add("four", 4);
counts.Add("two", 2);
counts.Add("three", 3);

Desc tarafından sipariş:

foreach (KeyValuePair<string, int> kvp in counts.OrderByDescending(key => key.Value))
{
// some processing logic for each item if you want.
}

Artan Sipariş:

foreach (KeyValuePair<string, int> kvp in counts.OrderBy(key => key.Value))
{
// some processing logic for each item if you want.
}

-2

Sözlüğü değere göre sıralayabilir ve sonucu aşağıdaki kodu kullanarak sözlüğe alabilirsiniz:

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

8
Sıralanan öğeleri tekrar bir sözlüğe koyarak, yeni sözlüğü numaralandırdığınızda bunların sıralanacağı garanti edilmez.
Marty Neal

2
Ve bu cevabı zaten cevaplandığında neden ekliyorsunuz?
nawfal

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.