Каков наилучший способ перебора словаря?

2628

Я видел несколько разных способов перебора словаря в C #. Есть ли стандартный способ?

Джейк Стюарт
источник
108
Я удивлен, что есть много ответов на этот вопрос, наряду с одним проголосовавшим 923 раза .. (использует foreach ofcourse) .. Я бы поспорил или, по крайней мере, добавил, что если вам придется перебирать словарь, скорее всего, вы использовать его неправильно / ненадлежащим образом. Я должен был сделать этот комментарий, потому что я видел злоупотребления в словарях способами, которые ИМХО были неуместны ... Да, могут быть редкие обстоятельства, когда вы перебираете словарь, а не ищите, вот что это разработано для .. Пожалуйста, имейте это в виду, прежде чем задаться вопросом, как перебрать словарь.
Викас Гупта
74
@VikasGupta Что бы вы предложили сделать с набором пар ключ-значение, если вы не знаете, какими будут ключи?
nasch
26
@displayName Если вы хотите что-то сделать с каждой парой ключ-значение, но у вас нет ссылки на ключи, которые можно использовать для поиска значений, вы бы перебирали словарь, верно? Я просто указывал, что бывают моменты, когда вы захотите это сделать, несмотря на утверждение Викаса о том, что это обычно неправильное использование.
nasch
35
Сказать, что это неправильное использование, означает, что есть лучшая альтернатива. Что это за альтернатива?
Кайл Делани
41
VikasGupta не прав, я могу подтвердить, что после многих лет высокопроизводительного программирования на C # и C ++ в не теоретических сценариях. Действительно, часто бывают случаи, когда можно создать словарь, сохранить уникальные пары ключ-значение, а затем выполнить итерацию по этим значениям, которые, как доказано, имеют уникальные ключи в коллекции. Создание любых других коллекций - это действительно неэффективный и дорогостоящий способ избежать повторения словаря. Пожалуйста, предоставьте хорошую альтернативу в качестве ответа на вопрос, разъясняющий вашу точку зрения, в противном случае ваш комментарий является довольно бессмысленным.
sɐunıɔ ןɐ qɐp

Ответы:

3715
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
Пабло Фернандес
источник
29
Что делать, если я точно не знаю тип ключа / значения в словаре. Использование var entryлучше в этом случае, и, таким образом, я проголосовал за этот ответ на второй взгляд вместо вышеупомянутого.
Озаир Кафрай
93
Использование @OzairKafray, varкогда вы не знаете тип, обычно плохая практика.
Nate
52
Этот ответ лучше, потому что Пабло по умолчанию не использовал ленивый кодер "var", который скрывает возвращаемый тип.
MonkeyWrench
119
@MonkeyWrench: Мех. Visual Studio знает, что это за тип; все, что вам нужно сделать, это навести курсор на переменную, чтобы узнать.
Роберт Харви
31
Насколько я понимаю, varработает только если тип известен во время компиляции. Если Visual Studio знает тип, то он также доступен для вас.
Кайл Делани
866

Если вы пытаетесь использовать универсальный словарь в 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 и выше, вы также можете использовать точный тип ваших ключей / значений здесь)

Иаков
источник
11
функция var наиболее необходима для вашего первого блока кода :)
nawfal
27
Я ценю, что этот ответ указывает, что вы можете перебирать ключи или значения явно.
Rotsiser Mho
11
Мне не нравится использование var здесь. Учитывая, что это просто синтаксический сахар, зачем его здесь использовать? Когда кто-то пытается прочитать код, ему нужно будет переходить по коду, чтобы определить тип myDictionary(если, конечно, это не настоящее имя). Я думаю, что использование var хорошо, когда тип очевиден, например, var x = "some string"но когда он не очевиден сразу, я думаю, что ленивое кодирование вредит читателю / рецензенту кода
Джеймс Вежба
8
varследует использовать экономно, по моему мнению. Особенно здесь, это не конструктивно: тип KeyValuePair, вероятно, имеет отношение к вопросу.
Синджай,
3
varимеет уникальную цель, и я не верю, что это «синтаксический» сахар. Целенаправленное использование является подходящим подходом.
Джошуа К
159

В некоторых случаях вам может понадобиться счетчик, который может быть предоставлен реализацией цикла for. Для этого LINQ предоставляет ElementAtследующее:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
Маурисио Федатто
источник
21
Чтобы использовать метод .ElementAt, помните: using System.Linq; Это не входит в FX. автоматически сгенерированные тестовые классы.
Tinia
14
Это путь, если вы изменяете значения, связанные с ключами. В противном случае выдается исключение при изменении и использовании foreach ().
Майк де Клерк
27
Не ElementAtявляется ли операция O (n)?
Артуро Торрес Санчес
162
Этот ответ совершенно не заслуживает такого количества голосов. Словарь не имеет неявного порядка, поэтому использование .ElementAtв этом контексте может привести к незначительным ошибкам. Гораздо более серьезный вопрос Артуро выше. Вы будете повторять время словаря, dictionary.Count + 1приводящее к сложности O (n ^ 2) для операции, которая должна быть только O (n). Если вам действительно нужен индекс (в противном случае вы, вероятно, в первую очередь используете неправильный тип коллекции), вам следует dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})вместо этого выполнять итерацию, а не использовать ее .ElementAtвнутри цикла.
спонсор
5
ElementAt - o (n) операция! Шутки в сторону? Это пример того, как вы не должны это делать. Эти много голосов?
Мукеш Адхварью
96

Зависит от того, хотите ли вы получить ключи или значения ...

Из 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);
}
Дж Хили
источник
82

Как правило, спрашивать «лучший способ» без определенного контекста - это все равно, что спрашивать, какой цвет лучше ?

С одной стороны, есть много цветов и нет лучшего цвета. Это зависит от необходимости и часто от вкуса тоже.

С другой стороны, есть много способов перебора словаря в 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а не пункт выбора.
Мафия
@Mafii Ты уверен? Значения, возвращаемые OrderBy, не относятся к типу KeyValuePair, у них нет Valueполя. Точный тип, который я вижу здесь IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Возможно, вы имели в виду что-то еще? Можете ли вы написать полную строку, показывающую, что вы имеете в виду (и проверить это)?
Стефан Гурихон
Я думаю, что этот ответ содержит то, что я имею в виду: stackoverflow.com/a/141105/5962841, но поправьте меня, если я что-то
напутал
1
@Mafii Перечитай весь мой ответ, объяснения между разделами кода говорят о контексте. Ответ, который вы упоминаете, похож на второй раздел кода в моем ответе (порядок не требуется). Там я просто написал, items.Valueкак вы предложили. В случае с четвертым разделом, который вы прокомментировали, Select()есть способ заставить foreachперечислять непосредственно значения в словаре вместо пар ключ-значение. Если вам как-то не нравится Select()в этом случае, вы можете предпочесть третий раздел кода. Смысл четвертого раздела - показать, что можно предварительно обработать коллекцию с помощью LINQ.
Стефан Гурихон
1
Если вы это сделаете, .Keys.Orderby()вы будете перебирать список ключей. Если это все, что вам нужно, хорошо. Если вам нужны значения, то в цикле вам придется запросить словарь по каждому ключу, чтобы получить значение. Во многих случаях это не будет иметь практического значения. В высокопроизводительном сценарии это будет. Как я написал в начале ответа: «Есть много способов (...), и нет лучшего пути. Это зависит от необходимости, а зачастую и от вкуса».
Стефан Гурихон
53

Я бы сказал, что foreach является стандартным способом, хотя он, очевидно, зависит от того, что вы ищете

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

Это то, что вы ищете?

Джордж Мауэр
источник
42
Хм, а название предмета "ценность" не слишком запутанно? Обычно вы используете синтаксис «value.Key» и «value.Value», который не очень понятен для тех, кто будет читать код, особенно если он не знаком с реализацией словаря .Net. ,
RenniePet
3
@RenniePet kvpобычно используется для именования экземпляров KeyValuePair при Перебор словарей и связанных с ними структур данных: foreach(var kvp in myDictionary){....
mbx
37

Вы также можете попробовать это в больших словарях для многопоточной обработки.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});
Onur
источник
8
@WiiMaxx и более важно, если эти предметы НЕ зависят друг от друга
Mafii
37

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
источник
5
Если вы используете .NET Framework, который по крайней мере до 4.7.2 не имеет деконструкции на KeyValuePair, попробуйте следующее:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith
29

Я ценю, что у этого вопроса уже было много ответов, но я хотел добавить небольшое исследование.

Итерация по словарю может быть довольно медленной по сравнению с итерацией по чему-то вроде массива. В моих тестах итерация по массиву занимала 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 секунд. Разумно ожидать, что тип данных влияет на то, насколько медленнее словарь.

Каковы мои выводы ?

  • Избегайте итераций по словарю, если вы можете, они существенно медленнее, чем итерации по массиву с теми же данными в нем.
  • Если вы решите перебрать словарь, не пытайтесь быть слишком умным, хотя медленнее вы можете сделать намного хуже, чем при использовании стандартного метода foreach.
Liath
источник
11
Вы должны измерить что-то вроде StopWatch вместо DateTime: hanselman.com/blog/…
Даже Mien
2
Не могли бы вы описать ваш тестовый сценарий, сколько элементов в вашем словаре, как часто вы запускали свой сценарий для расчета среднего времени, ...
WiiMaxx
3
Интересно, что вы получите разные результаты в зависимости от того, какие данные у вас есть в словаре. Выполняя итерацию по словарю, функция Enumerator должна пропускать много пустых слотов в словаре, что делает его медленнее, чем итерация по массиву. Если словарь заполнен, пустых мест будет меньше, чем если он наполовину пуст.
Мартин Браун
26

Есть много вариантов. Мой личный фаворит - KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

Вы также можете использовать ключи и коллекции значений

Тео
источник
14

С .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;
    }
}
Павел
источник
9
Это неверно .NET 4.7 просто ValueTupleвстроен. Он доступен как пакет nuget для более ранних версий. Что еще более важно, C # 7.0+ необходим для того, Deconstructчтобы метод работал в качестве деконструктора var (fruit, number) in fruits.
Дэвид Арно
13

Начиная с 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}");
}
Домн Вернер
источник
10

Вы предложили ниже, чтобы повторить

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

К вашему сведению, foreachне работает, если значение имеет тип объекта.

Джордж Стокер
источник
4
Пожалуйста, уточните: foreachне будет работать, если какое значение имеет тип object? В противном случае это не имеет особого смысла.
Марк Л.
9

Простейшая форма для перебора словаря:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}
Рон
источник
9

Используя 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

sɐunıɔ ןɐ qɐp
источник
1
В чем причина предпочтения «удобных» кортежей парам «ключ-значение»? Я не вижу здесь выгоды. Ваш кортеж содержит ключ и значение, как и пара ключ-значение.
Мартен
4
Привет, Мартен, спасибо за твой вопрос. Основным преимуществом является читаемость кода без дополнительных усилий программирования. С KeyValuePair нужно всегда использовать форму kvp.Keyи kvp.Valueиспользовать соответственно ключ и значение. С помощью кортежей вы можете гибко называть ключ и значение по своему усмотрению, не используя дальнейшие объявления переменных внутри блока foreach. Например, вы можете назвать свой ключ как factoryName, а значение как models, что особенно полезно, когда вы получаете вложенные циклы (словари словарей): обслуживание кода становится намного проще. Просто попробуйте! ;-)
sɐunıɔ ןɐ qɐp
7

Я знаю, что это очень старый вопрос, но я создал несколько методов расширения, которые могут быть полезны:

    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
источник
6

Иногда, если вам нужно только перечислить значения, используйте коллекцию значений словаря:

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

Эндер
источник
+1 для повышения производительности. Действительно, итерация по самому словарю включает некоторые издержки, если все, что вам нужно, это значения. Это в основном связано с копированием значений или ссылок в структуры KeyValuePair.
Яан Варус
1
К вашему сведению, последний тест в этой ссылке - это проверка не того: он измеряет итерации по ключам, игнорируя значения, в то время как предыдущие два теста используют это значение.
Свежий полумесяц
5

Я нашел этот метод в документации для класса DictionaryBase на MSDN:

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

Это было единственное, что мне удалось правильно функционировать в классе, унаследованном от DictionaryBase.


источник
3
Это похоже на использование не словарной версии словаря ... т.е. до .NET Framework 2.0.
joedotnot
@ joed0tnot: это не универсальная версия, используемая для Hashtableобъектов
sɐunıɔ ןɐ qɐp
5

foreachсамый быстрый и если вы только перебираете ___.Values, он также быстрее

введите описание изображения здесь

Pixel_95
источник
1
Почему вы звоните ContainsKey()в forверсии? Это добавляет дополнительные издержки, которых нет в коде, с которым вы сравниваете. TryGetValue()существует, чтобы заменить этот точный шаблон "если ключ существует, получить элемент с ключом". Кроме того, если dictсодержит непрерывный диапазон целых чисел от 0до dictCount - 1, вы знаете, что индексатор не может выйти из строя; в противном случае dict.Keysэто то , что вы должны повторять. В любом случае, нет ContainsKey()/ TryGetValue()нужно. Наконец, пожалуйста, не размещайте скриншоты кода.
Бекон
3

Я воспользуюсь преимуществами .NET 4.0+ и предоставлю обновленный ответ на изначально принятый:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}
yazanpro
источник
3

Стандартный способ перебора словаря, согласно официальной документации на MSDN:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}
Ник
источник
2

Я написал расширение для цикла над словарем.

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 (...) { }... Кажется ненужным.
Мартен
1

Если, скажем, вы хотите перебрать коллекцию значений по умолчанию, я полагаю, что вы можете реализовать IEnumerable <>, где T - это тип объекта значений в словаре, а «this» - словарь.

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

источник
1

Как уже указывалось на этот ответ , 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)
{
    // ...
}
rucamzu
источник
0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));
Pixar
источник
2
В этом ответе должно быть больше обоснования / описания. Что AggregateObjectдобавить к KeyValuePair? Где находится «итерация», как было указано в вопросе?
Марк Л.
Select перебирает словарь и позволяет нам работать с каждым объектом. Это не так широко, как foreach, но я использовал это много. Действительно ли мой ответ заслуживает отрицательного ответа?
Pixar
Нет, Select использует итерацию для получения результата, но не является итератором. Типы вещей, для которых используется iteration ( foreach), особенно операции с побочными эффектами, выходят за рамки Linq, в том числе Select. Лямбда не будет работать, пока не aggregateObjectCollectionбудет фактически перечислена. Если этот ответ принимается за «первый путь» (т. Е. Используется перед прямой foreach), он поощряет плохие практики. В некоторых случаях могут быть полезны операции Linq перед итерацией словаря, но они не решают вопрос в том виде, в котором он задан.
Марк Л.
0

Просто хотел добавить свои 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 } );
Alex
источник
5
Копирование всей коллекции без всякой причины не улучшит производительность. Это значительно замедлит код, а также удвоит объем памяти, занимаемой кодом, который практически не потребляет дополнительной памяти.
Serv
Я избегаю метода List.ForEach с побочными эффектами: усиливает foreachвидимость побочных эффектов там, где они есть.
user2864740
0

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);
}
Шео Дайал Сингх
источник
0

Если вы хотите использовать для цикла, вы можете сделать это:

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, и если ваша коллекция может измениться после итерации, она защитит вас от ошибки «коллекция изменилась», и это решение будет иметь лучшую производительность, чем при использовании ElementAt.
Сечкин Дургай
Я согласен, что избегание исключений «Коллекция была изменена» является одной из причин, чтобы сделать это, хотя этот особый случай не был указан в вопросе, и всегда можно было это сделать foreach (var pair in dictionary.ToArray()) { }. Тем не менее, я думаю, что было бы хорошо разъяснить в ответе конкретные сценарии, в которых можно использовать этот код, и последствия этого.
Бекон
да, вы правы, но здесь есть ответ с ElementAt, и он имеет очень высокую репутацию, и я ввел этот ответ :)
Seçkin Durgay
-2

просто с 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 лет назад, во всяком случае.
Бекон
Просто для визуализации кода это действительно можно сделать так, как вы указали, я просто хотел представить это по-другому, и, с моей точки зрения, я прошу прощения за то, что не увидел ответ, объятие
Luiz Fernando Corrêa Leite
-4

в дополнение к постам с самым высоким рейтингом, где есть обсуждение между использованием

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
источник
5
Создание и копирование второго словаря не является правильным решением для удобства чтения кода. На самом деле, я бы сказал, что это усложнит понимание кода, потому что теперь вы должны спросить себя: «Почему последний парень создал второй словарь?» Если вы хотите быть более многословным, просто используйте первый вариант.
Мэтью Гуларт
Я просто хотел показать вам, что когда decl dict прежде для каждого, использование в foreach ясно из декларации
BigChief