값을 기준으로 사전을 어떻게 정렬합니까?


796

나는 종종 키와 값으로 구성된 사전을 값으로 정렬해야합니다. 예를 들어, 나는 단어와 해시의 해시를 가지고 있는데, 나는 빈도별로 주문하고 싶다.

SortedList내가 다시 말에 매핑 할 것인지, 단일 값 (예를 들어 주파수)에 대한 좋은이다.

값이 아닌 키를 기준으로 정렬 된 사전 순서. 일부는 커스텀 클래스에 의존 하지만 더 깨끗한 방법이 있습니까?


1
수락 된 답변에서와 같이 사전을 정렬하는 것 외에도 IComparer트릭을 수행하는 트릭을 만들 수도 있습니다 (사실 키를 허용하지만 키를 사용하면 값을 얻을 수 있음). ;-)
BrainSlugs83

답변:


520

사용하다:

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 이상을 대상으로하기 때문에이를 람다 구문으로 단순화 할 수 있습니다. 이는 동등하지만 짧습니다. .NET 2.0을 대상으로하는 경우 Visual Studio 2008 이상의 컴파일러를 사용하는 경우에만이 구문을 사용할 수 있습니다.

var myList = aDictionary.ToList();

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

26
나는이 솔루션 (감사합니다!)을 사용했지만 Michael Stum의 게시물 (및 John Timney의 코드 스 니펫)을 읽을 때까지 잠시 혼란 스러웠으며 myList는 사전에서 생성 된 KeyValuePairs 목록 인 보조 객체라는 것을 깨달았습니다. 그런 다음 정렬했습니다.
Robin Bennett

113
그것은 하나의 라이너입니다-중괄호가 필요하지 않습니다. 그것은는 다음과 같이 다시 작성할 수 있습니다myList.Sort((x,y)=>x.Value.CompareTo(y.Value));
아르 니스 Lapsa

25
내림차순으로 정렬하려면 비교에서 x와 y를 전환하십시오. myList.Sort ((x, y) => y.Value.CompareTo (x.Value));
Arturo

8
ToList 확장 방법에는 Linq가 필요하다는 점에 주목할 가치가 있다고 생각합니다.
Ben

17
여러분은 이것을 복잡하게 만드는 것에 대해 겁이납니다. 사전은 이미 구현 IEnumerable되어 있으므로 다음과 같이 정렬 된 목록을 얻을 수 있습니다.var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
BrainSlugs83

523

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;

또한 상위 10, 20 10 % 등을 선택할 수 있다는 점에서 유연성이 뛰어납니다. 또는에 대한 단어 빈도 색인을 사용하는 경우 절도 type-ahead포함 할 수 있습니다 StartsWith.


15
sortedDict를 Dictionary <string, int>로 다시 변경하려면 어떻게해야합니까? 여기에 새로운 SO 질문이 게시되었습니다 : stackoverflow.com/questions/3066182/…
Kache

1
슬프게도 이것은 .net framework 2.0 (LINQ 없음) 때문에 VS2005에서 작동하지 않습니다. Bambrick의 답변도있는 것이 좋습니다.
스몰 캣

22
사전을 반복해도 KeyValuePair가 삽입 된 순서와 동일하게 "당겨"지지 않을 수 있기 때문에 항상 작동하는지 확실하지 않습니다. Ergo, LINQ에서 orderby를 사용하더라도 Dictionary는 삽입 된 요소의 순서를 변경할 수 있으므로 중요하지 않습니다. 일반적으로 예상대로 작동하지만 특히 큰 사전의 경우 보증이 없습니다.
Bozydar Sobczak

16
반환 유형은 IEnumerable<KeyValuePair<TKey, TValue>>또는 이어야합니다 OrderedDictionary<TKey, TValue>. 또는 SortedDictionary처음부터를 사용해야합니다 . 평원의 Dictionary경우 MSDN에는 "항목이 반환되는 순서는 정의되어 있지 않습니다"라고 명확하게 표시되어 있습니다. @ rythos42의 최신 편집은 비난하는 것 같습니다. :)
Boris B.

16
모든 제안을 무시하십시오 .ToDictionary- 표준 사전을 정렬 순서 보장하지 않습니다
AlexFoxGill

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

6
이 솔루션이 왜 인기가 없는지 잘 모르겠습니다. 아마도 .NET 3.5가 필요하기 때문일까요?
Contango

33
이것은 좋은 해결책이지만, 끝나는 세미콜론 바로 앞에 다음이 있어야합니다. .ToDictionary (pair => pair.Key, pair => pair.Value);
theJerm

3
@theJerm은 정렬 된 항목을 사전에 다시 넣어서 순서가 보장됩니까? 오늘 작동 할 수도 있지만 보장되지는 않습니다.
nawfal

1
4.5 프레임 워크를 사용하여 사전으로 캐스트 필요 가 없음을 확인했습니다 .
Jagd

12
사전이 정렬되지 않았으므로 사전으로 캐스트되지 않아야합니다. KeyValuePairs가 원하는 순서대로 유지된다는 보장은 없습니다.
David DeMar

165

둘러보고 C # 3.0 기능을 사용하면 다음과 같이 할 수 있습니다.

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

이것은 내가 본 가장 깨끗한 방법이며 해시를 처리하는 루비 방법과 비슷합니다.


KeyValuePairs를 ComboBox에 추가하는 동안 사전을 정렬하려고했습니다 ... 감사!
Jason Down

6
이 구문을 사용할 때 System.Linq 네임 스페이스를 추가하는 것을 잊지 마십시오.
M. Dudley

4
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)-당신의 대답에 작은 추가 :) 감사합니다, btw :)
Andrius Naruševičius

7
@ AndriusNaruševičius : 사전에 결과 항목을 다시 추가하면 사전이 특정 방식으로 정렬되도록 보장되지 않으므로 순서가 삭제됩니다 .
또는 매퍼

이것은 편리했다. 다른 방향으로 나아가려면 어떻게 뒤집을 수 있습니까?
Dan Hastings

158

값을 기준으로 사전을 정렬하고 다시 저장하면됩니다 (따라서 사전에 값이 순서대로 나옵니다).

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

물론 정확하지는 않지만 작동합니다.


8
내림차순으로 정렬하려면 OrderByDescending을 사용할 수도 있습니다.
Mendokusai

나를 위해 일했지만 약간 변경해야했습니다. Dictionary <key, value> dict = dict.OrderBy (x => x.Value) .ToDictionary (x => x.Key, x => x.Value);
Josh

17
이 "작동"은 보장되지 않습니다. 구현 세부 사항입니다. 다른 시간에는 작동하지 않아도됩니다. 답이 잘못되었습니다.
nawfal

4
사전 출력에는 특정 정렬 순서가 보장되지 않습니다.
Roger Willcocks

5
나는 이것을 프로덕션 코드에서 보는 것을 매우 걱정할 것입니다. 보장되지 않으며 언제든지 변경 될 수 있습니다. 실용적 솔루션에서 벗어나지 않고 데이터 구조 imo에 대한 이해가 부족하다는 것을 보여줍니다.
jamespconnor

61

높은 수준에서 전체 사전을 살펴보고 각 값을 보는 것 외에 다른 선택은 없습니다.

아마도 이것이 도움이 될 것입니다 : http://bytes.com/forum/thread563638.html John Timney의 복사 / Pasting :

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-> 문자열> nextPair stringfirstPair-> 문자열> firstPair
아트

2
완벽한 비 Linq 솔루션. Linq가 문제를 해결하는 데 절대적으로 필요하지 않은 경우에도 사람들이 Linq를 사용해야 할 필요성을 어떻게 느끼는지 결코 놀라지 않습니다. C # 3을 사용하면 정렬을 단순화하여 람다를 사용할 수 있다고 생각합니다. myList.Sort ((x, y) => x.Value.CompareTo (y.Value));

25

어쨌든 사전을 정렬 할 수 없습니다. 그들은 실제로 주문되지 않았습니다. 사전에 대한 보장은 키 및 값 콜렉션이 반복 가능하며 인덱스 또는 키로 값을 검색 할 수 있지만 특정 순서를 보장하지는 않습니다. 따라서 이름 값 쌍을 목록으로 가져와야합니다.


2
정렬 된 사전은 키-값 쌍의 목록을 생성 할 수 있습니다.
재귀

1
@recursive 모든 사전은 그것을 산출해야합니다. 내 대답은 정확하지만 불완전합니다 (더 나은 예제를 수행했을 수 있음)는 잘못된 사전 아래에 투표하여 원래 사전의 중복 값에 대한 예외가 발생합니다 (키는 고유하며 값은 보장되지 않습니다) )가 될 수 있습니다
로저 윌콕스

5
사전은 정렬 할 수 없기 때문에 이것이 답입니다. 키를 해시하고 매우 빠른 탐색 작업을 수행 할 수 있습니다.
Paulius Zaliaduonis

@NetMage 예. 그러나 문제의 다른 부분은 그들이 가치별로 주문하기를 원한다는 것입니다. 그리고 당신은 키와 값을 교환해야만 할 수 있습니다. 그리고 Value는 반드시 고유하지는 않지만 Key는 반드시 있어야합니다.
Roger Willcocks

예, 그러나 귀하의 답변은 절대적인 진술로 인해 잘못된 것 같습니다.
NetMage

21

사전에서 항목을 정렬하지 않습니다. .NET의 Dictionary 클래스는 해시 테이블로 구현됩니다.이 데이터 구조는 정의별로 정렬 할 수 없습니다.

컬렉션별로 키를 반복해야 할 경우-Binary Search Tree로 구현 된 SortedDictionary를 사용해야합니다.

그러나 소스 구조는 다른 필드로 정렬되기 때문에 소스 구조는 관련이 없습니다. 여전히 빈도별로 정렬하고 관련 필드 (빈도)별로 정렬 된 새 컬렉션에 배치해야합니다. 따라서이 모음에서 빈도는 키이고 단어는 값입니다. 많은 단어가 같은 빈도를 가질 수 있고 (키로 사용하려고 할 때) Dictionary 또는 SortedDictionary를 사용할 수 없습니다 (고유 키 필요). 그러면 SortedList가 남습니다.

주 / 처음 사전의 원래 항목에 대한 링크를 유지해야한다고 주장하는 이유를 이해하지 못합니다.

콜렉션의 오브젝트가 더 복잡한 구조 (더 많은 필드)를 가지고 있고 여러 다른 필드를 키로 사용하여 효율적으로 액세스 / 정렬 할 수 있어야하는 경우, 기본 스토리지로 구성된 사용자 정의 데이터 구조가 필요할 것입니다. O (1) 삽입 및 제거 (LinkedList) 및 여러 인덱싱 구조 (Dictionaries / SortedDictionaries / SortedLists)를 지원합니다. 이 인덱스는 복잡한 클래스의 필드 중 하나를 키로 사용하고 LinkedList의 LinkedListNode에 대한 포인터 / 참조를 값으로 사용합니다.

인덱스를 기본 컬렉션 (LinkedList)과 동기화하려면 삽입 및 제거를 조정해야하며 제거는 꽤 비쌀 것입니다. 이것은 데이터베이스 인덱스의 작동 방식과 유사합니다. 조회에는 환상적이지만 많은 삽입과 삭제를 수행해야 할 때 부담이됩니다.

위의 모든 사항은 조회 무거운 처리를 수행하려는 경우에만 정당화됩니다. 한 번만 주파수별로 정렬하여 출력 해야하는 경우 (익명) 튜플 목록을 생성 할 수 있습니다.

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

또는 재미를 위해 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

VB.NET을 사용하여 컨트롤 SortedDictionary에 바인딩 할 목록 정렬 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

정렬 된 사전을 얻는 가장 쉬운 방법은 내장 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 정렬 버전이 포함됩니다 sections


7
의견에서 언급했듯이 SortedDictionary키별로 정렬합니다. OP는 값을 기준으로 정렬하려고합니다. SortedDictionary이 경우 도움이되지 않습니다.
Marty Neal

글쎄 ... 그 / 그녀가 할 수 있다면, 그 값들을 키로 설정하십시오. 나는 작업 시간을 정하고 sorteddictionary()적어도 1 마이크로 초 이상 승리했으며, 관리하기가 훨씬 쉽습니다 (사전과 0과 유사하게 쉽게 상호 작용하고 관리하는 것으로 다시 변환하는 오버 헤드가 이미 0 sorteddictionary임).
mbrownnyc

2
@mbrownnyc-아니, 그렇게하려면 가치가 고유하다는 가정이나 전제 조건이 필요하지만 보장되지는 않습니다.
Roger Willcocks

6

당신이 원하는 모든 것이 "일시적인"목록을 값으로 정렬하는 것이라면 다른 대답도 좋습니다. 당신으로 분류 사전 갖고 싶어하지만, Key그것을 자동으로 동기화 으로 분류되는 다른 사전으로 Value당신이 사용할 수있는, Bijection<K1, K2>클래스를 .

Bijection<K1, K2> 두 개의 기존 사전으로 컬렉션을 초기화 할 수 있으므로 그 중 하나를 정렬하지 않고 다른 하나를 정렬하려면 다음과 같은 코드로 궤적을 만들 수 있습니다

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

당신은 dict어떤 일반 사전처럼 (구현 IDictionary<K, V>) 사용할 수 있습니다 , 그리고 dict.Inverse정렬 된 "역"사전을 얻기 위해 호출 합니다 Value.

Bijection<K1, K2>Loyc.Collections.dll의 일부 이지만 원하는 경우 소스 코드 를 자신의 프로젝트에 간단히 복사 할 수 있습니다.

참고 : 값이 같은 여러 키가있는 경우, 당신은 사용할 수 없습니다 Bijection,하지만 당신은 수동으로 일반 사이에 동기화 할 수 Dictionary<Key,Value>BMultiMap<Value,Key>.


http://stackoverflow.com/questions/268321과 유사 하지만 각 사전을 SortedDictionary로 바꿀 수 있습니다. 대답은 중복 값을 지원하지 않는 것으로 보이지만 (1 대 1로 가정).
crokusek

3

다음과 같이 사전이 있다고 가정하십시오.

   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) 당신은 사용할 수 있습니다 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

실제로 C #에서 Dictionaries dint에는 sort () 메서드가 있습니다. 값별로 정렬하는 데 더 관심이 있기 때문에 키를 제공 할 때까지 값을 얻을 수 없습니다. 간단히 말해 LINQ의 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
}

당신은 하나의 트릭을 할 수 있습니다

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

또는

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

또한 저장하는 값의 종류에 따라 달라집니다.
단일 (문자열, 정수) 또는 다중 (목록, 배열, 사용자 정의 클래스),
단일 값을 목록으로 만들 수 있으면 정렬을 적용하십시오.
사용자가 클래스를 정의한 경우 해당 클래스는 IComparable을 구현
ClassName: IComparable<ClassName>하고 compareTo(ClassName c) LINQ보다 속도가 빠르고 객체 지향적이므로 재정의 해야합니다 .


0

필요한 네임 스페이스 : 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로 주문 :

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

Asc로 주문 :

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

-2

아래 코드를 사용하여 사전을 값별로 정렬하고 결과를 사전으로 가져올 수 있습니다.

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

8
정렬 된 항목을 사전에 다시 넣으면 더 이상 새 사전을 열거 할 때 정렬 될 수 없습니다.
Marty Neal

2
그리고 이것이 이미 답변되었을 때 왜이 답변을 추가합니까?
nawfal

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.