etc./StackOverFlow

사전을 반복하는 가장 좋은 방법은 무엇입니까? [닫은]

청렴결백한 만능 재주꾼 2021. 11. 11. 07:57
반응형

질문자 :Jake Stewart


C#에서 사전을 반복하는 몇 가지 다른 방법을 보았습니다. 표준 방법이 있습니까?



foreach(KeyValuePair<string, string> entry in myDictionary) { // do something with entry.Value or entry.Key }

Pablo Fernandez

다른 언어의 연관 배열을 사용하는 것처럼 C#에서 일반 사전을 사용하려는 경우:

 foreach(var item in myDictionary) { foo(item.Key); bar(item.Value); }

또는 키 컬렉션에 대해서만 반복해야 하는 경우 다음을 사용하십시오.

 foreach(var item in myDictionary.Keys) { foo(item); }

마지막으로 값에만 관심이 있는 경우:

 foreach(var item in myDictionary.Values) { foo(item); }

( var 키워드는 선택적인 C# 3.0 이상의 기능입니다. 여기서 키/값의 정확한 유형을 사용할 수도 있습니다.)


Jacob

경우에 따라 for-loop 구현에서 제공할 수 있는 카운터가 필요할 수 있습니다. 이를 위해 LINQ는 다음을 가능하게 하는 ElementAt

 for (int index = 0; index < dictionary.Count; index++) { var item = dictionary.ElementAt(index); var itemKey = item.Key; var itemValue = item.Value; }

Maurício Fedatto

키를 쫓는지 값을 쫓는지에 따라...

MSDN Dictionary(TKey, TValue) 클래스 설명에서:

 // When you use foreach to enumerate dictionary elements, // the elements are retrieved as KeyValuePair objects. Console.WriteLine(); foreach( KeyValuePair<string, string> kvp in openWith ) { Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value); } // To get the values alone, use the Values property. Dictionary<string, string>.ValueCollection valueColl = openWith.Values; // The elements of the ValueCollection are strongly typed // with the type that was specified for dictionary values. Console.WriteLine(); foreach( string s in valueColl ) { Console.WriteLine("Value = {0}", s); } // To get the keys alone, use the Keys property. Dictionary<string, string>.KeyCollection keyColl = openWith.Keys; // The elements of the KeyCollection are strongly typed // with the type that was specified for dictionary keys. Console.WriteLine(); foreach( string s in keyColl ) { Console.WriteLine("Key = {0}", s); }

J Healy

일반적으로 특정 컨텍스트 없이 "가장 좋은 방법"을 묻는 것은 가장 좋은 색상이 무엇인지 묻는 것과 같습니다.

한편으로는 많은 색상이 있지만 가장 좋은 색상은 없습니다. 그것은 필요에 따라 다르며 종종 맛에도 달려 있습니다.

반면에 C#에서 사전을 반복하는 방법에는 여러 가지가 있으며 가장 좋은 방법은 없습니다. 그것은 필요에 따라 다르며 종종 맛에도 달려 있습니다.

가장 직관적인 방법

 foreach (var kvp in items) { // key is kvp.Key doStuff(kvp.Value) }

값만 필요한 경우( item 이라고 부를 수 있으며 kvp.Value 보다 읽기 쉽습니다).

 foreach (var item in items.Values) { doStuff(item) }

특정 정렬 순서가 필요한 경우

일반적으로 초보자는 Dictionary의 열거 순서에 대해 놀란다.

LINQ는 순서(및 기타 여러 가지)를 지정할 수 있는 간결한 구문을 제공합니다. 예:

 foreach (var kvp in items.OrderBy(kvp => kvp.Key)) { // key is kvp.Key doStuff(kvp.Value) }

다시 한 번 값만 필요할 수도 있습니다. LINQ는 다음과 같은 간결한 솔루션도 제공합니다.

  • 값에 대해 직접 반복( item kvp.Value 보다 읽기 쉽습니다)
  • 그러나 키로 정렬

여기있어:

 foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)) { doStuff(item) }

이 예제에서 수행할 수 있는 실제 사용 사례가 더 많습니다. 특정 주문이 필요하지 않은 경우 "가장 간단한 방법"을 따르십시오(위 참조)!


Stéphane Gourichon

나는 foreach가 표준 방법이라고 말하고 싶지만 분명히 당신이 찾고있는 것에 달려 있습니다.

 foreach(var kvp in my_dictionary) { ... }

그게 당신이 찾고있는거야?


George Mauer

C# 7.0해체자를 도입했으며 .NET Core 2.0+ 애플리케이션을 사용하는 경우 구조체 KeyValuePair<> 에는 이미 Deconstruct() 되어 있습니다. 따라서 다음을 수행할 수 있습니다.

 var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } }; foreach (var (key, value) in dic) { Console.WriteLine($"Item [{key}] = {value}"); } //Or foreach (var (_, value) in dic) { Console.WriteLine($"Item [NO_ID] = {value}"); } //Or foreach ((int key, string value) in dic) { Console.WriteLine($"Item [{key}] = {value}"); }

여기에 이미지 설명 입력


Jaider

다중 스레드 처리를 위해 큰 사전에서 이것을 시도할 수도 있습니다.

 dictionary .AsParallel() .ForAll(pair => { // Process pair.Key and pair.Value here });

Onur

나는 이 질문에 이미 많은 응답을 받은 것에 감사하지만 약간의 조사를 하고 싶었습니다.

사전을 반복하는 것은 배열과 같은 것을 반복하는 것과 비교할 때 다소 느릴 수 있습니다. 내 테스트에서 배열에 대한 반복은 0.015003초가 걸렸지만 사전(동일한 수의 요소 포함)에 대한 반복은 2.4배 긴 0.0365073초가 걸렸습니다! 훨씬 더 큰 차이점을 보았지만. 비교를 위해 List는 0.00215043초 사이에 있었습니다.

그러나 그것은 사과와 오렌지를 비교하는 것과 같습니다. 내 요점은 사전을 반복하는 것이 느리다는 것입니다.

사전은 조회에 최적화되어 있으므로 이를 염두에 두고 두 가지 방법을 만들었습니다. 하나는 단순히 foreach를 수행하고 다른 하나는 키를 반복한 다음 조회합니다.

 public static string Normal(Dictionary<string, string> dictionary) { string value; int count = 0; foreach (var kvp in dictionary) { value = kvp.Value; count++; } return "Normal"; }

이것은 키를 로드하고 대신 반복합니다(키를 string[]으로 가져오려고 시도했지만 그 차이는 무시할 수 있었습니다.

 public static string Keys(Dictionary<string, string> dictionary) { string value; int count = 0; foreach (var key in dictionary.Keys) { value = dictionary[key]; count++; } return "Keys"; }

이 예에서 일반적인 foreach 테스트는 0.0310062를 사용했고 키 버전은 0.2205441을 사용했습니다. 모든 키를 로드하고 모든 조회를 반복하는 것은 분명히 훨씬 느립니다!

최종 테스트를 위해 여기에서 키를 사용하면 어떤 이점이 있는지 확인하기 위해 10번 반복했습니다(이 시점에서 저는 그저 궁금했습니다).

진행 상황을 시각화하는 데 도움이 되는 RunTest 메서드가 있습니다.

 private static string RunTest<T>(T dictionary, Func<T, string> function) { DateTime start = DateTime.Now; string name = null; for (int i = 0; i < 10; i++) { name = function(dictionary); } DateTime end = DateTime.Now; var duration = end.Subtract(start); return string.Format("{0} took {1} seconds", name, duration.TotalSeconds); }

여기에서 일반적인 foreach 실행은 0.2820564초가 소요되었습니다(예상대로 단일 반복에 소요된 시간보다 약 10배 더 깁니다). 키를 반복하는 데 2.2249449초가 걸렸습니다.

추가하기 위해 편집됨: 다른 답변 중 일부를 읽으면 사전 대신 사전을 사용하면 어떻게 될지 의문이 생겼습니다. 이 예에서 배열은 0.0120024초, 목록은 0.0185037초, 사전은 0.0465093초가 소요되었습니다. 데이터 유형에 따라 사전이 얼마나 느린지에 따라 달라질 것으로 예상하는 것이 합리적입니다.

내 결론은 무엇입니까 ?

  • 가능하면 사전을 반복하지 마십시오. 동일한 데이터가 있는 배열을 반복하는 것보다 훨씬 느립니다.
  • 사전을 반복하기로 선택한 경우 너무 영리하게 시도하지 마십시오. 속도가 느리면 표준 foreach 방법을 사용하는 것보다 훨씬 더 나쁠 수 있습니다.

Liath

많은 옵션이 있습니다. 개인적으로 가장 좋아하는 것은 KeyValuePair입니다.

 Dictionary<string, object> myDictionary = new Dictionary<string, object>(); // Populate your dictionary here foreach (KeyValuePair<string,object> kvp in myDictionary) { // Do some interesting things }

키 및 값 컬렉션을 사용할 수도 있습니다.


theo

이 답변 에서 이미 지적했듯이 KeyValuePair<TKey, TValue> 는 .NET Core 2.0, .NET Standard 2.1 및 .NET Framework 5.0(미리 보기)에서 시작 Deconstruct 메서드를 구현합니다.

KeyValuePair 불가지론적 방식으로 사전을 반복할 수 있습니다.

 var dictionary = new Dictionary<int, string>(); // ... foreach (var (key, value) in dictionary) { // ... }

rucamzu

.NET Framework 4.7 에서는 분해 를 사용할 수 있습니다.

 var fruits = new Dictionary<string, int>(); ... foreach (var (fruit, number) in fruits) { Console.WriteLine(fruit + ": " + number); }

이 코드가 낮은 C# 버전에서 작동하도록 하려면 System.ValueTuple NuGet package 추가하고 어딘가에 작성하세요.

 public static class MyExtensions { public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value) { key = tuple.Key; value = tuple.Value; } }

Pavel

C# 7부터 개체를 변수로 분해할 수 있습니다. 나는 이것이 사전을 반복하는 가장 좋은 방법이라고 믿습니다.

예시:

이를 분해하는 KeyValuePair<TKey, TVal> 에 대한 확장 메서드를 만듭니다.

 public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value) { key = pair.Key; value = pair.Value; }

다음 방식으로 모든 Dictionary<TKey, TVal>

 // Dictionary can be of any types, just using 'int' and 'string' as examples. Dictionary<int, string> dict = new Dictionary<int, string>(); // Deconstructor gets called here. foreach (var (key, value) in dict) { Console.WriteLine($"{key} : {value}"); }

Domn Werner

아래에서 반복하도록 제안했습니다.

 Dictionary<string,object> myDictionary = new Dictionary<string,object>(); //Populate your dictionary here foreach (KeyValuePair<string,object> kvp in myDictionary) { //Do some interesting things; }

참고로 값이 객체 유형이면 foreach


Khushi

사전을 반복하는 가장 간단한 형식:

 foreach(var item in myDictionary) { Console.WriteLine(item.Key); Console.WriteLine(item.Value); }

Ron

foreach 가 가장 빠르며 ___.Values 대해서만 반복하면 더 빠릅니다.

여기에 이미지 설명 입력


Pixel_95

C# 7을 사용하여 솔루션의 모든 프로젝트에 이 확장 메서드 를 추가합니다.

 public static class IDictionaryExtensions { public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>( this IDictionary<TKey, TValue> dict) { foreach (KeyValuePair<TKey, TValue> kvp in dict) yield return (kvp.Key, kvp.Value); } }


이 간단한 구문을 사용하십시오.

 foreach (var(id, value) in dict.Tuples()) { // your code using 'id' and 'value' }


또는 이 것, 원한다면

 foreach ((string id, object value) in dict.Tuples()) { // your code using 'id' and 'value' }


전통적인 장소 대신

 foreach (KeyValuePair<string, object> kvp in dict) { string id = kvp.Key; object value = kvp.Value; // your code using 'id' and 'value' }


확장 메서드는 IDictionary<TKey, TValue> KeyValuePair 를 강력한 형식의 tuple 로 변환하여 이 새로운 편리한 구문을 사용할 수 있도록 합니다.

필요한 사전 항목을 tuples 로 변환하므로 전체 사전을 tuples 로 변환하지 않으므로 이와 관련된 성능 문제는 없습니다.

KeyValuePair 직접 사용하는 것과 비교 tuple 을 생성하기 위한 확장 메서드를 호출하는 데 약간의 비용만 KeyValuePair 의 속성 KeyValue 를 새 루프 변수에 할당하는 경우 문제가 되지 않아야 합니다.

실제로 이 새로운 구문은 특정 지점에서 사용하지 않을 수 있는 옵션이 있는 저수준 초고성능 시나리오를 제외하고 대부분의 경우에 매우 적합합니다.

확인: MSDN 블로그 - C# 7의 새로운 기능


sɐunıɔןɐqɐp

이것이 매우 오래된 질문이라는 것을 알고 있지만 유용할 수 있는 몇 가지 확장 방법을 만들었습니다.

 public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a) { foreach (KeyValuePair<T, U> p in d) { a(p); } } public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a) { foreach (T t in k) { a(t); } } public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a) { foreach (U u in v) { a(u); } }

이렇게 하면 다음과 같은 코드를 작성할 수 있습니다.

 myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}")); myDictionary.Keys.ForEach(key => Console.Write(key);); myDictionary.Values.ForEach(value => Console.Write(value););

boca

MSDN의 DictionaryBase 클래스에 대한 설명서에서 이 방법을 찾았습니다.

 foreach (DictionaryEntry de in myDictionary) { //Do some stuff with de.Value or de.Key }

이것은 DictionaryBase에서 상속된 클래스에서 올바르게 작동할 수 있는 유일한 것이었습니다.


Zannjaminderson

때때로 값만 열거해야 하는 경우 사전의 값 컬렉션을 사용합니다.

 foreach(var value in dictionary.Values) { // do something with entry.Value only }

가장 빠른 방법이라는 이 게시물에 의해 보고되었습니다. http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html


ender

.NET 4.0+를 활용하고 원래 허용된 답변에 대한 업데이트된 답변을 제공하겠습니다.

 foreach(var entry in MyDic) { // do something with entry.Value or entry.Key }

yazanpro

MSDN의 공식 문서에 따르면 사전을 반복하는 표준 방법은 다음과 같습니다.

 foreach (DictionaryEntry entry in myDictionary) { //Read entry.Key and entry.Value here }

Nick

사전을 반복하는 확장을 작성했습니다.

 public static class DictionaryExtension { public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) { foreach(KeyValuePair<T1, T2> keyValue in dictionary) { action(keyValue.Key, keyValue.Value); } } }

그러면 전화를 걸 수 있습니다.

 myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));

Steven Delrue

예를 들어 기본적으로 값 컬렉션을 반복하려는 경우 IEnumerable<>을 구현할 수 있다고 생각합니다. 여기서 T는 사전에 있는 값 개체의 유형이고 "this"는 사전입니다.

 public new IEnumerator<T> GetEnumerator() { return this.Values.GetEnumerator(); }

mzirino

for 루프를 사용하려면 다음을 수행할 수 있습니다.

 var keyList=new List<string>(dictionary.Keys); for (int i = 0; i < keyList.Count; i++) { var key= keyList[i]; var value = dictionary[key]; }

Seçkin Durgay

var dictionary = new Dictionary<string, int> { { "Key", 12 } }; var aggregateObjectCollection = dictionary.Select( entry => new AggregateObject(entry.Key, entry.Value));

Egor Okhterov

Dictionary< TKey, TValue > C#의 일반 컬렉션 클래스이며 키 값 형식으로 데이터를 저장합니다. 키는 고유해야 하며 null일 수 없는 반면 값은 중복 및 null일 수 있습니다. 사전의 각 항목은 다음과 같습니다. 키와 그 값을 나타내는 KeyValuePair< TKey, TValue > 구조로 취급됩니다. 따라서 요소를 반복하는 동안 요소 유형 KeyValuePair< TKey, TValue>를 취해야 합니다. 아래는 예시입니다.

 Dictionary<int, string> dict = new Dictionary<int, string>(); dict.Add(1,"One"); dict.Add(2,"Two"); dict.Add(3,"Three"); foreach (KeyValuePair<int, string> item in dict) { Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value); }

Sheo Dayal Singh

대부분의 답변이 foreach-loop와 관련이 있으므로 2센트를 추가하고 싶었습니다. 다음 코드를 살펴보십시오.

 Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>(); //Add some entries to the dictionary myProductPrices.ToList().ForEach(kvP => { kvP.Value *= 1.15; Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value)); });

이것은 '.ToList()'의 추가 호출을 추가하지만, 특히 큰 사전으로 작업하고 병렬로 실행하는 것이 불가능할 때 약간의 성능 향상이 있을 수 있습니다(여기서 foreach 대 someList.Foreach(){}). 옵션 / 전혀 효과가 없습니다.

또한 foreach 루프 내부의 'Value' 속성에 값을 할당할 수 없습니다. 반면에 'Key'도 조작할 수 있으므로 런타임에 문제가 발생할 수 있습니다.

키와 값을 "읽기"만 하려는 경우 IEnumerable.Select()를 사용할 수도 있습니다.

 var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

Alex

가장 좋은 대답은 물론입니다. 반복할 계획이라면 정확한 사전을 사용하지 마십시오 . Vikas Gupta가 질문 아래의 토론에서 이미 언급했듯이. 그러나 이 전체 스레드로서의 논의는 여전히 놀랍도록 좋은 대안이 부족합니다. 하나는:

 SortedList<string, string> x = new SortedList<string, string>(); x.Add("key1", "value1"); x.Add("key2", "value2"); x["key3"] = "value3"; foreach( KeyValuePair<string, string> kvPair in x ) Console.WriteLine($"{kvPair.Key}, {kvPair.Value}");

왜 많은 사람들이 사전 반복의 코드 냄새 고려 (예 : foreach는에 의해 (KeyValuePair는 <,>) : 그것은 매우 놀라운 일이지만, 거기에 약간의 인용이지만, 청소의 기본 원칙 코딩 : "Express는 의도" Robert C. Martin은 "Clean Code"에서 "의도를 드러내는 이름 선택"이라고 썼습니다. 분명히 이것은 너무 약합니다. " 모든 코딩 결정과 함께 의도를 표현(공개)"하는 것이 더 좋습니다. 내 표현. 좋은 첫 번째 소스가 부족합니다. 그러나 나는 거기에 있다고 확신합니다. 관련 원칙은 " 가장 놀라움의 원칙 "(= 최소 놀라움의 원칙)입니다.

이것이 사전을 반복하는 것과 관련이 있는 이유는 무엇입니까? 딕셔너리를 선택한다는 것은 키로 데이터를 찾기 위해 만들어진 데이터 구조를 선택하려는 의도를 표현한 것이다 . 요즘에는 이 목발이 필요하지 않은 키/값 쌍을 반복하려는 경우 .NET에 많은 대안이 있습니다.

게다가: 만약 당신이 무언가를 반복한다면, 당신은 아이템이 어떻게 주문되고 주문될 것으로 예상되는지에 대해 무언가를 밝혀야 합니다! AFAIK, 사전에는 순서 지정에 대한 사양이 없습니다(구현별 규칙만 해당). 대안은 무엇입니까?

TLDR:
SortedList : 컬렉션이 너무 커지지 않는 경우 간단한 솔루션은 SortedList<,> 키/값 쌍의 전체 인덱싱도 제공하는 사용하는 것입니다.

Microsoft에는 피팅 컬렉션을 언급하고 설명하는 긴 기사가 있습니다.
키 컬렉션

가장 중요한 것은 KeyedCollection <,> 및 SortedDictionary<,> 입니다. SortedDictionary <,>는 크기가 커지면 삽입만 하는 SortedList보다 약간 빠르지만 인덱싱이 부족하고 삽입을 위한 O(log n)이 다른 작업보다 우선시되는 경우에만 필요합니다. 삽입을 위해 O(1)이 정말로 필요하고 느린 반복을 허용하려면 간단한 Dictionary<,>를 유지해야 합니다. 분명히 가능한 모든 작업에 대해 가장 빠른 데이터 구조는 없습니다.

또한 ImmutableSortedDictionary <,>가 있습니다.

그리고 하나의 데이터 구조가 정확히 필요한 것이 아니라면 Dictionary<,> 또는 새로운 ConcurrentDictionary <,>에서 파생하고 명시적인 반복/정렬 기능을 추가하십시오!


Philm

사용 사이에 토론이 있는 가장 높은 순위의 게시물 외에도

 foreach(KeyValuePair<string, string> entry in myDictionary) { // do something with entry.Value or entry.Key }

또는

 foreach(var entry in myDictionary) { // do something with entry.Value or entry.Key }

초기화에서 사전 유형을 볼 수 있기 때문에 가장 완전한 것은 다음과 같습니다. kvp는 KeyValuePair입니다.

 var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x foreach(var kvp in myDictionary)//iterate over dictionary { // do something with kvp.Value or kvp.Key }

BigChief

출처 : http:www.stackoverflow.com/questions/141088/what-is-the-best-way-to-iterate-over-a-dictionary

반응형