C #에서 사전을 반복하는 몇 가지 방법을 보았습니다. 표준적인 방법이 있습니까?
C #에서 사전을 반복하는 몇 가지 방법을 보았습니다. 표준적인 방법이 있습니까?
답변:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var
는 유형이 일반적으로 나쁜 습관인지 모르는 경우에 사용 합니다.
var
컴파일 타임에 유형이 알려진 경우에만 작동합니다. Visual Studio에서 유형을 알고 있으면 해당 유형도 찾을 수 있습니다.
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 기능 이상, 당신은 또한 여기에 키 / 값의 정확한 유형을 사용할 수 있습니다)
myDictionary
합니다 (실제 이름이 아닌 한). 나는 타입이 명백 할 때 var를 사용하는 것이 좋다고 생각한다. var x = "some string"
그러나 즉시 명백하지 않을 때 나는 코드 판독기 / 검토
var
제 생각에는 드물게 사용해야합니다. 특히 여기서는 건설적이지 않습니다. 유형 KeyValuePair
이 질문과 관련이있을 수 있습니다.
var
독특한 목적을 가지고 있으며 그것이 '구문 적'설탕이라고 믿지 않습니다. 의도적으로 사용하는 것이 적절한 방법입니다.
경우에 따라 for 루프 구현에서 제공 할 수있는 카운터가 필요할 수 있습니다. 이를 위해 LINQ는 ElementAt
다음을 지원합니다.
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
ElementAt
O (n)이 작업은?
.ElementAt
가 없으므로이 컨텍스트에서 사용 하면 미묘한 버그가 발생할 수 있습니다. 위의 Arturo의 요점은 훨씬 더 심각합니다. 사전 dictionary.Count + 1
시간을 반복하여 O (n)이어야하는 연산에 대해 O (n ^ 2) 복잡성을 초래합니다. 실제로 인덱스가 필요한 경우 (아마도 처음에 잘못된 컬렉션 유형을 사용하고있는 경우) dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
대신 반복 .ElementAt
하여 루프 내부에서 사용하지 않아야합니다 .
키 또는 값 뒤에 있는지 여부에 따라 다릅니다.
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);
}
일반적으로 특정 컨텍스트없이 "최상의 방법"을 요구 하는 것은 최상의 색상이 무엇인지 묻는 것과 같습니다 .
한 손으로 많은 색상이 있으며 최상의 색상이 없습니다. 그것은 필요와 종종 맛에 달려 있습니다.
반면에 C #에서 Dictionary를 반복하는 방법은 여러 가지가 있으며 최선의 방법은 없습니다. 그것은 필요와 종종 맛에 달려 있습니다.
foreach (var kvp in items)
{
// key is kvp.Key
doStuff(kvp.Value)
}
값만 필요한 경우 (을 호출 할 수 item
있으며보다 읽기 쉽습니다 kvp.Value
).
foreach (var item in items.Values)
{
doStuff(item)
}
일반적으로 초보자는 사전의 열거 순서에 놀랐습니다.
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)
}
이 예제에서 수행 할 수있는 더 많은 실제 사용 사례가 있습니다. 특정 주문이 필요하지 않은 경우 "가장 간단한 방법"(위 참조)을 고수하십시오!
.Values
은 select 절이 아니 어야합니다 .
Value
필드 가 없습니다 . 내가 본 정확한 유형은 IOrderedEnumerable<KeyValuePair<TKey, TValue>>
입니다. 아마도 다른 의미가 있습니까? 당신이 의미하는 바를 보여주는 완전한 줄을 작성할 수 있습니까?
items.Value
제안한대로 썼습니다 . 네 번째로 언급 한 섹션의 경우 키-값 쌍 대신 사전의 값을 직접 열거 Select()
하는 방법 foreach
입니다. 이 경우에 마음에 들지 않으면 Select()
세 번째 코드 섹션을 선호 할 수 있습니다. 네 번째 섹션의 요점은 LINQ로 컬렉션을 사전 처리 할 수 있음을 보여주는 것입니다.
.Keys.Orderby()
하면 키 목록을 반복하게됩니다. 그게 전부라면 괜찮습니다. 값이 필요한 경우 루프에서 각 키의 사전을 쿼리하여 값을 가져와야합니다. 많은 시나리오에서 실질적인 차이는 없습니다. 고성능 시나리오에서는 가능합니다. 내가 대답의 시작 부분에 쓴 것처럼 : "많은 방법이 있지만 (...) 최선의 방법은 없습니다. 그것은 또한 필요에 따라 맛에 달려 있습니다."
나는 foreach가 표준 방법이라고 말하고 싶지만 분명히 당신이 찾고있는 것에 달려 있습니다.
foreach(var kvp in my_dictionary) {
...
}
찾고 계십니까?
kvp
은 일반적으로 사전 및 관련 데이터 구조를 반복 할 때 KeyValuePair 인스턴스의 이름을 지정하는 데 사용됩니다 foreach(var kvp in myDictionary){...
.
C # 7.0 에는 Deconstructors가 도입되었으며 .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}");
}
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
이 질문에 이미 많은 답변이 있었지만 약간의 연구를하고 싶었습니다.
배열과 같은 것을 반복하는 것과 비교할 때 사전을 반복하는 것이 다소 느릴 수 있습니다. 내 테스트에서 배열에 대한 반복은 0.015003 초가 걸리고 사전 (동일한 수의 요소가있는)에 대한 반복은 0.0365073 초가 걸리는 2.4 배입니다! 나는 훨씬 더 큰 차이를 보았지만. 비교를 위해 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";
}
이것은 키를로드하고 대신 반복합니다 (키를 문자열로 가져 오려고 시도했지만 차이는 무시할 만했습니다.
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 초가 걸렸습니다.
추가 편집 : 다른 답변 중 일부를 읽으면 Dictionary 대신 Dictionary를 사용하면 어떻게 될지 의문이 생겼습니다. 이 예제에서 배열은 0.0120024 초, 목록 0.0185037 초 및 사전 0.0465093 초가 걸렸습니다. 데이터 유형이 사전의 속도를 얼마나 느리게 만드는지 예상하는 것이 합리적입니다.
내 결론은 무엇입니까 ?
함께 .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;
}
}
ValueTuple
내장되어 있습니다. 이전 버전의 너겟 패키지로 제공됩니다. 더 중요한 것은 C # 7.0+가 Deconstruct
메소드가 해체 자로 작동 하기 위해 필요하다는 것 입니다 var (fruit, number) in fruits
.
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}");
}
아래에서 반복하도록 제안했습니다.
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
//Do some interesting things;
}
참고로, foreach
값이 object 유형 인 경우 작동하지 않습니다.
foreach
경우 작동하지 않습니다 어떤 값이 타입이다 object
? 그렇지 않으면 이것은 의미가 없습니다.
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'
}
확장 메서드는 KeyValuePair
의 IDictionary<TKey, TValue>
형식을 강력한 형식으로 변환 tuple
하여이 새로운 편안한 구문을 사용할 수 있습니다.
필수 사전 항목을로만 tuples
변환하므로 전체 사전을로 변환하지 tuples
않으므로 이와 관련된 성능 문제가 없습니다.
tuple
사용 하는 것과 비교 하여 확장 메소드를 호출하는 데 약간의 비용이 듭니다 .KeyValuePair
직접 적습니다. 어쨌든 KeyValuePair
의 속성 Key
과 Value
새 루프 변수를 지정하는 경우에는 문제가되지 않습니다 .
실제로,이 새로운 구문은 저수준 초 고성능 시나리오를 제외하고는 대부분의 경우에 매우 적합합니다.이 시나리오에서는 여전히 특정 지점에서 사용하지 않는 옵션이 있습니다.
이 체크 아웃 : MSDN 블로그 - 새로운 C의 기능 # 7
kvp.Key
과 kvp.Value
각각의 키와 값을 사용해야합니다 . 튜플을 사용하면 foreach 블록 내에서 추가 변수 선언을 사용하지 않고도 키와 값의 이름을 유연하게 지정할 수 있습니다. 예를 들어 키의 이름을으로 factoryName
, 값을으로 models
지정할 수 있습니다. 이는 중첩 루프 (사전의 사전)를 얻을 때 특히 유용합니다. 코드 유지 관리가 훨씬 쉬워집니다. 시도해보십시오! ;-)
나는 이것이 매우 오래된 질문이라는 것을 알고 있지만 유용한 확장 방법을 만들었습니다.
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););
때때로 값을 열거해야하는 경우 사전의 값 콜렉션을 사용하십시오.
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
MSDN의 DictionaryBase 클래스 설명서에서이 방법을 찾았습니다.
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
이것은 DictionaryBase에서 상속받은 클래스에서 올바르게 작동 할 수있는 유일한 방법이었습니다.
Hashtable
객체에 사용되는 비 제네릭 버전입니다.
ContainsKey()
에 for
버전? 이것은 비교중인 코드에없는 추가 오버 헤드를 추가합니다. TryGetValue()
정확한 "키가 있으면 항목을 키로 가져옵니다"패턴을 대체합니다. 경우 또한, dict
의 정수의 연속 된 범위에 포함 0
로를 dictCount - 1
, 당신은 인덱서가 실패 할 수있어, 그렇지 않으면 dict.Keys
반복해야합니다. 어느 쪽이든은, 아니 ContainsKey()
/ TryGetValue()
이 필요했다. 마지막으로, 코드 스크린 샷을 게시하지 마십시오.
사전을 반복하는 확장 프로그램을 작성했습니다.
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));
ForEach
방법을 정의했습니다 foreach (...) { }
... 불필요한 것 같습니다.
말하자면, 기본적으로 값 컬렉션을 반복하려면 IEnumerable <>을 구현할 수 있다고 생각합니다. 여기서 T는 사전의 값 개체 유형이며 "this"는 사전입니다.
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
대부분의 대답은 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 vs someList.Foreach () {} ). 옵션 / 전혀 영향을 미치지 않습니다.
또한 foreach 루프 내에서 'Value'속성에 값을 할당 할 수 없습니다. 반면에 '키'도 조작 할 수있어 런타임시 문제가 발생할 수 있습니다.
키와 값을 "읽고"싶을 때 IEnumerable.Select ()를 사용할 수도 있습니다.
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
foreach
부작용 가시성을 강제로 올리십시오.
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
추가 KeyValuePair
합니까? 질문에서 요청한 "반복"은 어디에 있습니까?
foreach
아니지만 많이 사용했습니다. 내 대답은 진정으로 공감할 가치가 있는가?
Select
사용 하여 결과에 영향을 주지만 반복자 자체는 아닙니다. 반복 ( foreach
), 특히 부작용이있는 작업에 사용되는 작업 유형은을 포함하여 Linq의 범위를 벗어납니다 Select
. 람다 aggregateObjectCollection
는 실제로 열거 될 때까지 실행되지 않습니다 . 이 답변을 "첫 번째 경로"(즉, 직선 앞에 사용 foreach
)로 사용하면 나쁜 관행을 장려합니다. 상황에 따라 사전을 반복 하기 전에 도움이 되지만 요청한대로 질문을 해결하지 못하는 Linq 작업이있을 수 있습니다 .
Dictionary <TKey, TValue> c #의 제네릭 컬렉션 클래스이며 데이터를 키 값 형식으로 저장합니다 .Key는 고유해야하며 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);
}
for 루프를 사용하려면 다음을 수행하십시오.
var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
var key= keyList[i];
var value = dictionary[key];
}
foreach
루프 와 있기 때문에 더 나쁜 성능을 new List<string>(dictionary.Keys)
반복 할 것이다 dictionary.Count
당신도 스스로 반복하는 기회를하기 전에 시간을. "가장 좋은 방법"을 요구하는 것은 주관적이라는 점을 제외하고는 이것이 질문이 찾는 "가장 좋은 방법"또는 "표준 방법"으로 어떤 자격이 있는지는 알 수 없습니다. "당신은 루프에 사용하려는 경우 ..."에 내가 함께 카운터 것 " 하지 마십시오 용도 for
루프를."
foreach (var pair in dictionary.ToArray()) { }
있습니다. 아직도, 나는이 코드를 사용하려는 특정 시나리오와 그 의미를 명확하게 이해하는 것이 좋을 것이라고 생각합니다.
linq로 간단
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "American Ipa");
dict.Add(2, "Weiss");
dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );
사용 사이에 토론이있는 최고 순위 게시물 외에도
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
}