Я видел несколько разных способов перебора словаря в C #. Есть ли стандартный способ?
Я видел несколько разных способов перебора словаря в C #. Есть ли стандартный способ?
Ответы:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var entry
лучше в этом случае, и, таким образом, я проголосовал за этот ответ на второй взгляд вместо вышеупомянутого.
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
в этом контексте может привести к незначительным ошибкам. Гораздо более серьезный вопрос Артуро выше. Вы будете повторять время словаря, dictionary.Count + 1
приводящее к сложности O (n ^ 2) для операции, которая должна быть только O (n). Если вам действительно нужен индекс (в противном случае вы, вероятно, в первую очередь используете неправильный тип коллекции), вам следует dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
вместо этого выполнять итерацию, а не использовать ее .ElementAt
внутри цикла.
Зависит от того, хотите ли вы получить ключи или значения ...
Из Dictionary(TKey, TValue)
описания класса MSDN :
// 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 #, и лучшего способа нет. Это зависит от необходимости и часто от вкуса тоже.
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
а не пункт выбора.
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 представил деконструкторы, и если вы используете приложение .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 раза больше! Хотя я видел гораздо большие различия. Для сравнения, список был где-то между 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. Загрузка всех ключей и перебор всех поисков явно медленнее!
Для финального теста я выполнил свою итерацию десять раз, чтобы увидеть, есть ли какие-либо преимущества в использовании ключей здесь (к этому моменту мне было просто любопытно):
Вот метод 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 секунды (примерно в десять раз дольше, чем одна итерация - как и следовало ожидать). Итерация по клавишам заняла 2,2249449 секунд.
Отредактировано, чтобы добавить: Чтение некоторых других ответов заставило меня задуматься, что произойдет, если я воспользуюсь словарем вместо словаря. В этом примере массив занял 0,0120024 секунды, список 0,0185037 секунд и словарь 0,0465093 секунд. Разумно ожидать, что тип данных влияет на то, насколько медленнее словарь.
Каковы мои выводы ?
Есть много вариантов. Мой личный фаворит - KeyValuePair
Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary)
{
// Do some interesting things
}
Вы также можете использовать ключи и коллекции значений
С .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
встроен. Он доступен как пакет nuget для более ранних версий. Что еще более важно, 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
не работает, если значение имеет тип объекта.
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
Я нашел этот метод в документации для класса DictionaryBase на MSDN:
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();
}
Как уже указывалось на этот ответ , KeyValuePair<TKey, TValue>
реализуетDeconstruct
метод, начиная с .NET Core 2.0, .NET Standard 2.1 и .NET Framework 5.0 (предварительный просмотр).
При этом можно перебирать словарь в KeyValuePair
произвольной форме:
var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
// ...
}
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
добавить к KeyValuePair
? Где находится «итерация», как было указано в вопросе?
foreach
, но я использовал это много. Действительно ли мой ответ заслуживает отрицательного ответа?
Select
использует итерацию для получения результата, но не является итератором. Типы вещей, для которых используется iteration ( foreach
), особенно операции с побочными эффектами, выходят за рамки Linq, в том числе Select
. Лямбда не будет работать, пока не aggregateObjectCollection
будет фактически перечислена. Если этот ответ принимается за «первый путь» (т. Е. Используется перед прямой foreach
), он поощряет плохие практики. В некоторых случаях могут быть полезны операции Linq перед итерацией словаря, но они не решают вопрос в том виде, в котором он задан.
Просто хотел добавить свои 2 цента, так как большинство ответов касаются цикла foreach. Пожалуйста, взгляните на следующий код:
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 () {} ), особенно при работе с большими словарями и параллельной работе нет опция / не будет иметь никакого эффекта вообще.
Также обратите внимание, что вы не сможете присваивать значения свойству Value внутри цикла foreach. С другой стороны, вы также сможете манипулировать «ключом», что может привести к проблемам во время выполнения.
Если вы просто хотите «прочитать» ключи и значения, вы также можете использовать IEnumerable.Select ().
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
foreach
видимость побочных эффектов там, где они есть.
Dictionary <TKey, TValue> Это универсальный класс коллекции в c #, в котором хранятся данные в формате значения ключа. Ключ должен быть уникальным и не может быть нулевым, в то время как значение может быть дублированным и нулевым. Поскольку каждый элемент в словаре рассматривается как структура 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);
}
Если вы хотите использовать для цикла, вы можете сделать это:
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}") );
ToList()
потому что ForEach()
он определен только в List<>
классе, но зачем все это, а не просто foreach (var pair in dict) { }
? Я бы сказал, что это еще проще и не имеет таких же последствий для памяти и производительности. Это точное решение уже было предложено в этом ответе от 3,5 лет назад, во всяком случае.
в дополнение к постам с самым высоким рейтингом, где есть обсуждение между использованием
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
}