Питання Який найкращий спосіб переглянути словник?


Я бачив кілька різних способів перебору словника в C #. Чи існує стандартний спосіб?


1949
2017-09-26 18:20


походження


Я здивований, що багато відповідей на це питання, разом з одним виправленим 923 рази .. (використовується для будь-якого курсу) .. Я сперечаюсь, або принаймні додати, що якщо вам потрібно послідовно переглянути словник, можливо, ви використовуючи його неправильно / недоречно .. Мені доводилося робити цей коментар, тому що я бачив використання словників у способах, які IMHO не підходили ... Так, можуть бути рідкісні обставини, коли ви повторюєте словник, а не шукаєте, що саме він призначений для ... Будь ласка, майте це на увазі, перш ніж цікаво, як ітерації над словником. - Vikas Gupta
@WikasGupta Що б ви запропонували зробити щось із набором пар ключ-значення, коли ви не знаєте, які ключі будуть? - nasch
@nash: myDictionary.Keys дасть вам збірку, що містить ключі в myDictionary. - displayName
@displayName Якщо ви хочете щось зробити з кожною парою ключових значень, але не маєте посилання на клавіші, які слід використовувати для пошуку значень, то вам доведеться прокручувати словник, чи не так? Я просто наголошував, що може бути час, коли ви хотіли б це зробити, незважаючи на твердження Вікаса, що це, як правило, невірне використання. - nasch
Сказати, що неправильне використання означає, що є краща альтернатива. Що таке альтернатива? - Kyle Delaney


Відповіді:


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

2900
2017-09-26 18:22



Що робити, якщо я точно не знаю тип ключа / значення у словнику. Використовуючи var entry в цьому випадку краще, і таким чином я проголосував ця відповідь на другий вигляд, а не на вищезгаданий. - Ozair Kafray
@OzairKafray використовуючи var коли ти не знаєш тип, це загалом погана практика. - Nate
Ця відповідь є вищою, оскільки Пабло не використав за замовчуванням ненормальний кодер "var", який зачіпає тип повернення. - MonkeyWrench
@MonkeyWrench: Мех. Visual Studio знає, що таке тип; все, що вам потрібно зробити, це навести курсор на змінну, щоб дізнатись про це. - Robert Harvey♦
Як я це розумію, var Працює тільки тоді, коли цей тип відомий під час компіляції. Якщо Visual Studio знає тип, то він також доступний для вас. - Kyle Delaney


Якщо ви намагаєтеся використовувати загальний словник у 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 та вище, ви також можете використовувати точний тип ваших ключів / значень тут)


646
2017-09-26 18:22



Функція var найбільше потрібна для вашого першого блоку коду :) - nawfal
Я вдячний, що ця відповідь вказує на те, що ви можете повторювати клавіші чи значення явним чином. - Rotsiser Mho
Мені не подобається використовувати var тут. Враховуючи, що це просто синтаксичний цукор, чому його використовувати тут? Коли хтось намагається прочитати код, їм доведеться перестрибнути навколо коду, щоб визначити тип myDictionary (якщо це не фактичне ім'я курсу). Я думаю, що використання var - це добре, коли тип очевидний, наприклад var x = "some string" але коли це не відразу очевидно, я думаю, це ліниве кодування, яке шкодить читачеві коду / рецензенту - James Wierzba
var слід використовувати економно, на мій погляд. Особливо тут, це не конструктивно: тип KeyValuePair швидше за все стосується питання. - Sinjai


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

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

107
2018-03-10 20:44



Щоб використовувати метод '.ElementAt', пам'ятайте: використовуючи System.Linq; Це не включено в fx. автоматично зібрані тестові класи. - Tinia
Це спосіб перейти, якщо ви змінюєте значення, пов'язані з клавішами. В іншому випадку при зміні та використанні foreach () буде виключено виключення. - Mike de Klerk
Будьте обережні, використовуючи це. Дивіться тут: stackoverflow.com/a/2254480/253938 - RenniePet
Чи не так? ElementAt O (n) операція? - Arturo Torres Sánchez
Ця відповідь абсолютно не заслуговує на те, що так багато вертольотів. Словник не має неявного порядку, тому використовуючи .ElementAt в цьому контексті може призвести до витончених помилок. Набагато серйознішою є тема Артуро вище. Ви будете повторювати словник dictionary.Count + 1 час, що призводить до складності O (n ^ 2) для операції, яка повинна бути лише O (n). Якщо вам дійсно потрібний індекс (якщо ви робите це, то, напевно, ви використовуєте неправильний тип збірки), то вам слід повторити dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value}) а не використовувати .ElementAt всередині петлі - spender


Залежить від того, чи є ви після клавіш або значень ...

З 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);
}

75
2017-09-26 18:27





Взагалі, запит на "найкращий спосіб" без конкретного контексту, як запитати, який найкращий колір.

З одного боку, є багато кольорів, і немає кращого кольору. Це залежить від необхідності, а часто і за смаком.

З іншого боку, існує безліч способів ітерації над словником в 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)
}

Є багато інших реальних випадків використання, які ви можете зробити з цих прикладів. Якщо вам не потрібне певне замовлення, просто дотримуйтесь "найпростішого способу" (див. Вище)!


55
2017-08-10 11:15



Останній повинен бути .Values а не вибірка. - Mafii
@ Мафіі Ви впевнені? Значення, що повертаються OrderBy, не є типом KeyValuePair, їх немає Value поле Точний тип я бачу тут IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Можливо, ви мали на увазі щось інше? Чи можете ви написати повну лінію, яка показує, що ви маєте на увазі (і протестувати)? - Stéphane Gourichon
Я думаю, що ця відповідь містить те, що я маю на увазі: stackoverflow.com/a/141105/5962841 але виправте мене, якщо я щось збентежив - Mafii
@Mafii Повторно прочитайте мій цілковиту відповідь, пояснення між секціями коду говорять контексту. Вказана вами відповідь на зразок другого розділу коду у моїй відповіді (замовлення не потрібно). Там я тільки що написав items.Value як ви запропонували. У випадку четвертого розділу, про який ви прокоментували, Select() це спосіб викликати foreach для переліку безпосередньо на значення в словнику замість пари ключових значень. Якщо як-то вам не подобається Select()в цьому випадку ви можете віддати перевагу третій розділ коду. Підсумок четвертого розділу - показати, що можна попередньо обробити колекцію за допомогою LINQ. - Stéphane Gourichon
Якщо ти зробиш .Keys.Orderby() ви прослідуєте список клавіш. Якщо це все, що вам потрібно, добре. Якщо вам потрібні значення, то в циклі вам доведеться запитати словник по кожній клавіші, щоб отримати значення. У багатьох сценаріях це не буде мати практичної різниці. У високоефективному сценарії це буде. Як я писав на початку відповіді: "Є багато способів (...), і немає найкращого способу, це залежить від потреби, і часто також на смак". - Stéphane Gourichon


Я б сказав, що foreach є стандартним способом, хоча це, очевидно, залежить від того, що ви шукаєте

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

Це те, що ви шукаєте?


36
2017-09-26 18:22



Гм, не називає елемент "значення" досить заплутаним? Зазвичай ви використовуєте синтаксис типу "value.Key" і "value.Value", який не дуже інтуїтивно зрозумілий для будь-кого, хто буде читати цей код, особливо якщо вони не знайомі з тим, як вводять .Net Dictionary. . - RenniePet
@ РенніПет kvp зазвичай використовується для іменування KeyValuePair при повторенні словників та пов'язаних структур даних: foreach(var kvp in myDictionary){.... - mbx


Ви також можете спробувати це на великих словниках для багатопотокової обробки.

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

28
2018-06-11 13:32



@ WiiMaxx і більш важливо, якщо ці елементи НЕ залежать один від одного - Mafii


Є багато варіантів. Мій особистий улюблений ключ від KeyValuePair

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

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

Ви також можете використовувати Колекції клавіш та цінностей


22
2017-09-26 18:22





Я ціную, що на це питання вже було багато відповідей, але я хотів би кинути невелике дослідження.

Ітерація над словником може бути досить повільною у порівнянні з повторенням щось на зразок масиву. У моїх тестах ітерація по масиву зайняла 0.015003 секунди, тоді як ітерація над словником (з такою ж кількістю елементів) зайняла 0.0365073 секунди, що в 2.4 рази довше! Хоча я бачив набагато більші відмінності. Для порівняння список був десь посередині на 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";
}

При цьому прикладі звичайний 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);
}

Тут звичайний пробіжок на кожному кроці зайняв 0,2820564 секунди (приблизно в десять разів довше, ніж було здійснено одну ітерацію - як і слід було очікувати). Ітерація над клавішами зайняла 2.2249449 секунд.

Відредаговано, щоб додати: Читання деяких інших відповідей задав мені питання, що станеться, якщо б я використовував словник, а не словник. У цьому прикладі масив займає 0.0120024 секунд, список 0.0185037 секунд і словник 0.0465093 секунд. Це розумно очікувати, що тип даних впливає на те, наскільки повільним є словник.

Які мої висновки??

  • Уникайте повторення словника, якщо можете, вони суттєво повільні, ніж повторення масиву з однаковими даними.
  • Якщо ви вирішите прослідкувати за словником, не намагайтеся бути надто розумним, хоча повільніше ви можете зробити набагато гірше, ніж використовувати стандартний метод foreach.

22
2017-07-30 10:54



Ви повинні виміряти щось типу StopWatch замість DateTime: hanselman.com/blog/... - Even Mien
чи могли б ви, будь ласка, описати свій тестовий сценарій, скільки предметів у вашому словнику, як часто ви виконували свій сценарій для обчислення середнього часу ... - WiiMaxx
Цікаво, що ви отримаєте різні результати залежно від того, які дані у словнику. Під час його перетворення в словник, функція Enumerator повинна пропускати багато порожніх слотів у словнику, що призводить до того, що він буде повільніше, ніж повторення масиву. Якщо Словник повний, для пропуску буде менше порожніх слотів, ніж якщо воно наполовину порожнє. - Martin Brown


Ви запропонували нижче ітерацію

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

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

FYI foreach не працює, якщо значення є об'єкт типу.


9
2017-10-28 20:49



Будь-ласка, роз'ясніть: foreach не буде працювати, якщо котрий значення типу object? Інакше це не має сенсу. - Marc L.