Я видел несколько разных способов перебора словаря в C #. Есть ли стандартный способ?
& Лт;!- язык: c # - >
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
Если вы пытаетесь использовать общий словарь на 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 и выше, вы также можете использовать точный тип ваших ключей / значений здесь)
В некоторых случаях вам может понадобиться счетчик, который может быть предоставлен для реализации цикла. Для этого LINQ предоставляет [ElementAt
][1], который позволяет следующее:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
[1]: https://msdn.microsoft.com/en-us/library/bb299233(v = vs.110).aspx
Зависит от того, следите ли вы за ключами или значениями...
Из 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 #, и нет лучшего способа. Это зависит от необходимости и часто от вкуса тоже.
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)
}
Из этих примеров можно сделать гораздо больше реальных вариантов использования. Если вам не нужен конкретный заказ, просто придерживайтесь «самого простого способа» (см. Выше)!
Я бы сказал, что foreach - это стандартный способ, хотя он, очевидно, зависит от того, что вы ищете
foreach(var kvp in my_dictionary) {
...
}
Это то, что вы ищете?
Я ценю, что на этот вопрос уже было много ответов, но я хотел провести небольшое исследование.
Итерация по словарю может быть довольно медленной по сравнению с итерацией по чему-то вроде массива. В моих тестах итерация по массиву заняла 0,015003 секунды, тогда как итерация по словарю (с таким же количеством элементов) заняла 0,0365073 секунды, что в 2,4 раза больше! Хотя я видел гораздо большие различия. Для сравнения List < string > был где-то между 0,00215043 секундами.
Однако это все равно что сравнивать яблоки и апельсины. Я хочу сказать, что итерация словарей медленная.
Словари оптимизированы для поиска, поэтому я создал два метода. Один просто делает упрек, другой повторяет ключи, затем смотрит вверх.
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";
}
В этом примере обычный тест на лбу занял 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);
}
Здесь нормальный пробег на передней части занял 0,2820564 секунды (примерно в десять раз дольше, чем одна итерация - как и следовало ожидать). Итерация ключей заняла 2,2249449 секунды.
Отредактировано, чтобы добавить: Чтение некоторых других ответов заставило меня усомниться в том, что произойдет, если я использую Dictionary < object, object > вместо словаря < строка, строка >. В этом примере массив занял 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
}
Вы также можете использовать Коллекции ключей и ценностей
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}");
}
С .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;
}
}
Вы предложили ниже к итерации
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
//Do some interesting things;
}
К вашему сведению, foreach
не работает, если значение имеет тип объекта.
Используя 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);
}
}
& Лт; br > И используйте этот простой синтаксис
foreach (var(id, value) in dict.Tuples())
{
// your code using 'id' and 'value'
}
& Лт; br > Или этот, если хотите
foreach ((string id, object value) in dict.Tuples())
{
// your code using 'id' and 'value'
}
& Лт; br > Вместо традиционного
foreach (KeyValuePair<string, object> kvp in dict)
{
string id = kvp.Key;
object value = kvp.Value;
// your code using 'id' and 'value'
}
& Лт; br >
Метод расширения преобразует KeyValuePair
вашего IDictionary < TKey, TValue >
в сильно типизированный tuple
, позволяя вам использовать этот новый удобный синтаксис.
Он преобразует -just - необходимые записи словаря в tuples
, поэтому он НЕ преобразует весь словарь в tuples
, поэтому нет проблем с производительностью, связанных с этим.
Существует только небольшая стоимость, вызывающая метод расширения для создания tuple
по сравнению с прямым использованием KeyValuePair
, что НЕ должно быть проблемой, если вы назначаете свойства KeyValuePair
Key
и Value
к новым переменным цикла в любом случае.
На практике этот новый синтаксис очень хорошо подходит для большинства случаев, за исключением сценариев сверхвысокой производительности низкого уровня, где у вас все еще есть возможность просто не использовать его в этом конкретном месте.
Проверьте это: Блог MSDN - новые функции в C # 7 ,
Иногда, если вам нужно только перечислить значения, используйте коллекцию значений словаря:
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) { // Сделайте что-нибудь с de.Value или de.Key } < / code > < / pre >Это был единственный, который я смог правильно функционировать в классе, унаследованном от DictionaryBase.
Начиная с 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}");
}
Я знаю, что это очень старый вопрос, но я создал несколько методов расширения, которые могут быть полезны:
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););