Làm thế nào để bạn sắp xếp một từ điển theo giá trị?


796

Tôi thường phải sắp xếp một từ điển, bao gồm các khóa & giá trị, theo giá trị. Ví dụ, tôi có một hàm băm của các từ và tần số tương ứng, mà tôi muốn sắp xếp theo tần số.

Có một SortedListcái tốt cho một giá trị duy nhất (tần số nói), mà tôi muốn ánh xạ nó trở lại từ.

Sắp xếp thứ tự từ khóa theo khóa, không phải giá trị. Một số khu nghỉ mát cho một lớp tùy chỉnh , nhưng có một cách sạch hơn?


1
Ngoài việc chỉ sắp xếp từ điển (như trong câu trả lời được chấp nhận), bạn cũng có thể tạo một IComparermẹo thực hiện (đúng là nó chấp nhận một khóa để so sánh, nhưng với một khóa, bạn có thể nhận được một giá trị). ;-)
BrainSlugs83

Câu trả lời:


520

Sử dụng:

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

Vì bạn đang nhắm mục tiêu .NET 2.0 trở lên, bạn có thể đơn giản hóa điều này thành cú pháp lambda - tương đương, nhưng ngắn hơn. Nếu bạn đang nhắm mục tiêu .NET 2.0, bạn chỉ có thể sử dụng cú pháp này nếu bạn đang sử dụng trình biên dịch từ Visual Studio 2008 (hoặc cao hơn).

var myList = aDictionary.ToList();

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

26
Tôi đã sử dụng giải pháp này (Cảm ơn!) Nhưng đã bối rối trong một phút cho đến khi tôi đọc bài đăng của Michael Stum (và đoạn mã của anh ấy từ John Timney) và nhận ra rằng myList là một đối tượng thứ cấp, một danh sách KeyValuePairs, được tạo từ từ điển, và sau đó sắp xếp.
Robin Bennett

113
đó là một lớp lót - Bạn không cần niềng răng. nó có thể được viết lại thànhmyList.Sort((x,y)=>x.Value.CompareTo(y.Value));
Arnis Lapsa

25
Để sắp xếp giảm dần, chuyển x và y trên so sánh: myList.Sort ((x, y) => y.Value.CompareTo (x.Value));
Arturo

8
Tôi nghĩ rằng đáng chú ý rằng điều này đòi hỏi Linq cho phương thức mở rộng ToList.
Ben

17
Các bạn đang chờ đợi để làm phức tạp điều này - một từ điển đã được triển khai IEnumerable, vì vậy bạn có thể nhận được một danh sách được sắp xếp như thế này:var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
BrainSlugs83

523

Sử dụng 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;

Điều này cũng sẽ cho phép rất linh hoạt ở chỗ bạn có thể chọn 10, 20 10% hàng đầu, v.v. Hoặc nếu bạn đang sử dụng chỉ số tần số từ của mình type-ahead, bạn cũng có thể bao gồm StartsWithmệnh đề.


15
Làm cách nào tôi có thể thay đổi sortDict trở lại thành Từ điển <chuỗi, int>? Đã đăng câu hỏi SO mới tại đây: stackoverflow.com/questions/3066182/ từ
Kache

1
Đáng buồn là điều này không hoạt động trên VS2005 vì .net framework 2.0 ở đó (không có LINQ). Thật tốt khi có câu trả lời của Bambrick.
Smalcat

22
Tôi không chắc liệu nó có luôn hoạt động hay không bởi vì việc lặp lại từ điển không đảm bảo rằng KeyValuePairs được "kéo" theo đúng thứ tự mà chúng đã được chèn. Ergo, không thành vấn đề nếu bạn sử dụng orderby trong LINQ vì Từ điển có thể thay đổi thứ tự của các phần tử được chèn. Nó thường hoạt động như mong đợi nhưng KHÔNG CÓ BẢO ĐẢM, đặc biệt đối với các từ điển lớn.
Bozydar Sobczak

16
Kiểu trả về phải là IEnumerable<KeyValuePair<TKey, TValue>>hoặc một OrderedDictionary<TKey, TValue>. Hoặc người ta nên sử dụng một SortedDictionarytừ đầu. Đối với đồng bằng Dictionary, MSDN ghi rõ "Thứ tự trả lại các mục không được xác định.". Có vẻ như chỉnh sửa mới nhất của @ rythos42 là đáng trách. :)
Boris B.

16
Vui lòng bỏ qua tất cả các đề xuất của .ToDictionary- từ điển chuẩn không đảm bảo thứ tự sắp xếp
AlexFoxGill 15/03/13

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

6
Tôi không chắc tại sao giải pháp này không phổ biến hơn - có lẽ vì nó yêu cầu .NET 3.5?
Contango

33
Đây là một giải pháp tốt, nhưng nó nên có quyền này trước dấu chấm phẩy kết thúc: .ToDixi (cặp => cặp.Key, cặp => cặp. Giá trị);
theJerm

3
@theJerm bằng cách đưa các mục đã sắp xếp trở lại từ điển là thứ tự được đảm bảo rồi chứ? Nó có thể hoạt động ngày hôm nay, nhưng nó không được bảo đảm.
nawfal

1
Sử dụng khung 4.5, chỉ cần xác minh rằng nó không yêu cầu chuyển trở lại từ điển.
Jagd

12
Không nên bỏ lại từ điển vì từ điển không được đặt hàng. Không có gì đảm bảo KeyValuePairs sẽ theo thứ tự bạn muốn.
David DeMar

165

Nhìn xung quanh và sử dụng một số tính năng của C # 3.0, chúng tôi có thể thực hiện việc này:

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

Đây là cách sạch nhất tôi từng thấy và tương tự như cách xử lý băm của Ruby.


Tôi đã cố gắng sắp xếp một từ điển trong khi thêm KeyValuePairs vào ComboBox ... điều này thật tuyệt! Cảm ơn!
Jason Down

6
Đừng quên thêm không gian tên System.Linq khi sử dụng cú pháp này.
M. Dudley

4
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)- chỉ là một bổ sung nhỏ cho câu trả lời của bạn :) Cảm ơn, btw :)
Andrius Naruševičius

7
@ AndriusNaruševičius: Nếu bạn thêm các mục kết quả vào từ điển, bạn sẽ hủy đơn hàng, vì từ điển không được đảm bảo để được đặt hàng theo bất kỳ cách cụ thể nào .
HOẶC Mapper

Điều này rất tiện dụng. Làm thế nào nó có thể được đảo ngược để đi theo con đường khác?
Dan Hastings

158

Bạn có thể sắp xếp một Từ điển theo giá trị và lưu nó trở lại chính nó (để khi bạn thảo luận về nó, các giá trị sẽ xuất hiện theo thứ tự):

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

Chắc chắn, nó có thể không chính xác, nhưng nó hoạt động.


8
Bạn cũng có thể sử dụng OrderByDesceinating nếu bạn muốn sắp xếp vào một danh sách giảm dần.
Mendokusai

Làm việc cho tôi, mặc dù tôi phải thay đổi một chút thành: Dictionary <key, value> dict = dict.OrderBy (x => x.Value) .ToDixi (x => x.Key, x => x.Value);
Josh

17
"Làm việc" này không được đảm bảo. Đây là một chi tiết thực hiện. Nó không cần phải làm việc khác. Trả lời sai, hạ cấp.
nawfal

4
Đầu ra từ điển KHÔNG được đảm bảo có bất kỳ thứ tự sắp xếp cụ thể nào.
Roger Willcocks

5
Tôi sẽ khá quan tâm để thấy điều này trong mã sản xuất. Nó không được đảm bảo và có thể thay đổi bất cứ lúc nào. Không phải là tôi né tránh các giải pháp thực dụng, nó chỉ cho thấy sự thiếu hiểu biết về cấu trúc dữ liệu imo.
jamespconnor

61

Ở cấp độ cao, bạn không có lựa chọn nào khác ngoài việc đi qua toàn bộ Từ điển và xem xét từng giá trị.

Có lẽ điều này giúp: http://bytes.com/forum/thread563638.html Sao chép / Dán từ 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);
    }
);

3
stringnextPair -> chuỗi> nextPair stringfirstPair -> chuỗi> firstPair
Nghệ thuật

2
Giải pháp phi Linq hoàn hảo. Tôi không bao giờ hết ngạc nhiên khi mọi người cảm thấy cần phải sử dụng Linq ngay cả khi nó hoàn toàn không bắt buộc phải giải quyết vấn đề. Với C # 3, tôi tin rằng bạn cũng có thể đơn giản hóa Sắp xếp để chỉ sử dụng lambda: myList.Sort ((x, y) => x.Value.CompareTo (y.Value));

25

Bạn sẽ không bao giờ có thể sắp xếp một từ điển nào. Họ không thực sự được ra lệnh. Các đảm bảo cho một từ điển là các bộ sưu tập khóa và giá trị có thể lặp lại được và các giá trị có thể được truy xuất theo chỉ mục hoặc khóa, nhưng không có đảm bảo cho bất kỳ thứ tự cụ thể nào. Do đó, bạn sẽ cần phải đưa cặp giá trị tên vào một danh sách.


2
Một từ điển được sắp xếp có thể mang lại một danh sách các cặp khóa-giá trị mặc dù.
đệ quy

1
@recursive Bất kỳ từ điển nên mang lại điều đó. Thật thú vị khi lưu ý rằng câu trả lời của tôi là đúng, nhưng không đầy đủ (có thể đã làm những gì mà các ví dụ tốt hơn đã làm) được bình chọn bên dưới một câu trả lời không hợp lệ sẽ dẫn đến ngoại lệ cho các giá trị trùng lặp trong từ điển gốc (khóa là duy nhất, giá trị không được đảm bảo được)
Roger Willcocks

5
Đây là câu trả lời, bởi vì Từ điển không thể sắp xếp. Nó băm phím và bạn có thể thực hiện thao tác tìm kiếm cực nhanh trên nó.
Paulius Zaliaduonis

@NetMage Có. Nhưng phần khác của vấn đề là họ muốn đặt hàng theo Giá trị. Và bạn chỉ có thể làm điều đó bằng cách hoán đổi Khóa và Giá trị. Và Giá trị không nhất thiết là duy nhất, nhưng Key phải như vậy.
Roger Willcocks

Có, nhưng tôi nghĩ rằng câu trả lời của bạn là không chính xác vì những tuyên bố tuyệt đối trong đó.
NetMage

21

Bạn không sắp xếp các mục trong Từ điển. Lớp từ điển trong .NET được triển khai dưới dạng hashtable - cấu trúc dữ liệu này không thể sắp xếp theo định nghĩa.

Nếu bạn cần có khả năng lặp lại bộ sưu tập của mình (bằng phím) - bạn cần sử dụng SortedDipedia, được triển khai như một Cây tìm kiếm nhị phân.

Tuy nhiên, trong trường hợp của bạn, cấu trúc nguồn không liên quan, bởi vì nó được sắp xếp theo một trường khác. Bạn vẫn sẽ cần sắp xếp nó theo tần số và đưa nó vào một bộ sưu tập mới được sắp xếp theo trường (tần số) có liên quan. Vì vậy, trong bộ sưu tập này, tần số là khóa và từ là giá trị. Vì nhiều từ có thể có cùng tần số (và bạn sẽ sử dụng nó làm khóa), bạn không thể sử dụng cả Từ điển cũng như SortedDipedia (chúng yêu cầu các khóa duy nhất). Điều này để lại cho bạn một SortedList.

Tôi không hiểu lý do tại sao bạn khăng khăng duy trì một liên kết đến mục gốc trong từ điển chính / đầu tiên của bạn.

Nếu các đối tượng trong bộ sưu tập của bạn có cấu trúc phức tạp hơn (nhiều trường hơn) và bạn cần có khả năng truy cập / sắp xếp chúng một cách hiệu quả bằng cách sử dụng một số trường khác nhau làm khóa - Bạn có thể sẽ cần một cấu trúc dữ liệu tùy chỉnh bao gồm bộ lưu trữ chính hỗ trợ chèn và loại bỏ O (1) (LinkedList) và một số cấu trúc lập chỉ mục - Từ điển / Sắp xếp từ điển / Sắp xếp theo thứ tự. Các chỉ mục này sẽ sử dụng một trong các trường từ lớp phức tạp của bạn làm khóa và con trỏ / tham chiếu đến LinkedListNode trong LinkedList làm giá trị.

Bạn sẽ cần phối hợp các phần chèn và xóa để giữ cho các chỉ mục của bạn được đồng bộ hóa với bộ sưu tập chính (LinkedList) và các phần xóa sẽ khá tốn kém. Điều này tương tự như cách các chỉ mục cơ sở dữ liệu hoạt động - chúng rất tuyệt vời cho việc tra cứu nhưng chúng trở thành gánh nặng khi bạn cần thực hiện nhiều thao tác chèn và xóa.

Tất cả những điều trên chỉ hợp lý nếu bạn định thực hiện một số xử lý nặng. Nếu bạn chỉ cần xuất chúng một khi được sắp xếp theo tần suất thì bạn có thể chỉ cần tạo một danh sách các bộ dữ liệu (ẩn danh):

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

Hoặc để giải trí, bạn có thể sử dụng một số ưu điểm của tiện ích mở rộng 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));

10

Sắp xếp SortedDictionarydanh sách để liên kết vào một ListViewđiều khiển bằng 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>

7

Cách dễ nhất để có được một Từ điển được sắp xếp là sử dụng lớp dựng sẵn SortedDictionary:

//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ẽ chứa phiên bản đã sắp xếp của sections


7
Như bạn đề cập trong bình luận của bạn, SortedDictionarysắp xếp theo các phím. OP muốn sắp xếp theo giá trị. SortedDictionarykhông giúp đỡ trong trường hợp này.
Marty Neal

Chà ... Nếu anh ấy / cô ấy (bạn) có thể, chỉ cần đặt các giá trị làm khóa. Tôi đã tính thời gian cho các hoạt động và sorteddictionary()luôn giành được ít nhất 1 micro giây và việc quản lý dễ dàng hơn nhiều (vì chi phí chuyển đổi trở lại thành một thứ dễ dàng tương tác và quản lý tương tự như Từ điển là 0 (đã là a sorteddictionary)).
mbrownnyc

2
@mbrownnyc - Không, làm điều đó đòi hỏi phải có giả định hoặc điều kiện tiên quyết rằng GIÁ TRỊ là duy nhất, không được bảo đảm.
Roger Willcocks

6

Các câu trả lời khác là tốt, nếu tất cả những gì bạn muốn là có một danh sách "tạm thời" được sắp xếp theo Giá trị. Tuy nhiên, nếu bạn muốn có một từ điển được sắp xếp theo Keyđó sẽ tự động đồng bộ hóa với một từ điển khác được sắp xếp theo Value, bạn có thể sử dụng Bijection<K1, K2>lớp .

Bijection<K1, K2> cho phép bạn khởi tạo bộ sưu tập với hai từ điển hiện có, vì vậy nếu bạn muốn một trong số chúng được sắp xếp và bạn muốn sắp xếp một bộ khác, bạn có thể tạo bộ sưu tập của mình bằng mã như

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

Bạn có thể sử dụng dictnhư bất kỳ từ điển thông thường nào (nó thực hiện IDictionary<K, V>), sau đó gọi dict.Inverseđể lấy từ điển "nghịch đảo" được sắp xếp theo Value.

Bijection<K1, K2>là một phần của Loyc.Collections.dll , nhưng nếu bạn muốn, bạn chỉ cần sao chép mã nguồn vào dự án của riêng bạn.

Lưu ý : Trong trường hợp có nhiều khóa có cùng giá trị, bạn không thể sử dụng Bijection, nhưng bạn có thể đồng bộ hóa thủ công giữa thông thường Dictionary<Key,Value>và a BMultiMap<Value,Key>.


Tương tự như http://stackoverflow.com/questions/268321 nhưng có thể thay thế mỗi Từ điển bằng SortedDipedia. Mặc dù các câu trả lời có vẻ không hỗ trợ các giá trị trùng lặp (giả sử 1 đến 1).
crokusek

3

Giả sử chúng ta có một từ điển như

   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) bạn có thể sử dụng 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

Trên thực tế, trong C #, từ điển dintaries có các phương thức sort (), vì bạn quan tâm hơn đến việc sắp xếp theo các giá trị, bạn không thể nhận được các giá trị cho đến khi bạn cung cấp khóa cho chúng, tóm lại, bạn cần lặp lại chúng, sử dụng Order By của LINQ,

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
}

bạn có thể làm một mẹo

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

hoặc là

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

nó cũng phụ thuộc vào loại giá trị bạn đang lưu trữ,
nó là đơn (như chuỗi, int) hoặc nhiều (như List, Array, lớp do người dùng xác định),
nếu bạn có thể tạo danh sách của nó sau đó áp dụng sắp xếp.
nếu người dùng định nghĩa lớp, thì lớp đó phải triển khai IComparable
ClassName: IComparable<ClassName>và ghi đè compareTo(ClassName c) vì chúng nhanh hơn LINQ và hướng đối tượng nhiều hơn.


0

Không gian tên bắt buộc: 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);

Đặt hàng theo desc:

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

Đặt hàng bởi Asc:

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

-2

Bạn có thể sắp xếp Từ điển theo giá trị và nhận kết quả trong từ điển bằng mã bên dưới:

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

8
Bằng cách đưa các mục được sắp xếp trở lại vào một từ điển, chúng không còn được đảm bảo để được sắp xếp khi bạn liệt kê từ điển mới.
Marty Neal

2
Và tại sao bạn thêm câu trả lời này khi điều này đã được trả lời?
nawfal

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.