Loading...
Dianov avatar Dianov 13 Точки

Nested dictionary

Здравейте, колеги! Доста често като решавам задачи, свърззани с речници, ми се налага да използвам вложен речник за стойност, но изпитвам затруднение с OrderBy функцията, когато в условието ми искат подредба засягаща някое property на ключовете или стойностите от вложения речник. Например ако имам Dictionary<string, Dictionary<string, int>> exampleDictionary = new Dictionary <string, Dictionary<string, int>>(); и трябва да извърша подредба по ключа на речника, а след това някаква подредба, засягаща ключовете/стойностите във вложения речник се затруднявам с Lambda израза? Ще се радвам ако някой от по-опитните колеги ми обясни или link-не материал, където мога да прочета как се прави. Благодаря предварително!

0
C# Fundamentals 03/01/2022 14:15:55
VasilKotsev avatar VasilKotsev 830 Точки

Имплементацията на речника (конкретно Dictionary<TKey, TVal>) е като хеш таблица, съответно не може да бъде сортиран. Това, което можеш да постигнеш с LINQ extension методите или с чист LINQ e:

IOrderedEnumerable<KeyValuePair<TKey,TVal>> 

Което после можеш да прожектираж към асоциативен масив:

.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)

По удачния вариант е да ползваш SortedDictionary<TKey, TVal>  ако ти трябва подредба по ключ, той е имплементиран като BST.

Все пак ако се налага да ползваш нормалния речник можеш да направиш нещо подобно:

var carsSoldByManufacturer = new Dictionary<string, IDictionary<string, int>>
{
    {
        "Toyota", new Dictionary<string, int>
        {
            { "Prius", 931_1321 },
            { "Sequoia", 50_013 },
            { "Land Cruiser", 413_2134 },
            { "Aygo", 124_3213 }
        }
    },
    {
        "Alfa Romeo", new Dictionary<string, int>
        {
            { "Guilia", 285_1334 },
            { "Stelvio", 388_9123 },
            { "4C Spider", 7_023 },
        }
    }
};

var leastSoldCarsOrderedByManufacturer = carsSoldByManufacturer
    .OrderBy(mnf => mnf.Key)
    .ToDictionary(
        mnf => mnf.Key,
        mnf => mnf.Value
            .OrderBy(model => model.Value)
            .ToDictionary(model => model.Key, model => model.Value)
    );

void PrintCarsSoldReport(Dictionary<string, Dictionary<string, int>> report)
{
    foreach (var manufacturer in report)
    {
        Console.WriteLine($"{manufacturer.Key}");
        foreach (var model in manufacturer.Value)
        {
            Console.WriteLine($" -{model.Key} -> {model.Value}");
        }
    }
}

PrintCarsSoldReport(leastSoldCarsOrderedByManufacturer);

В примера съм сортирал по бройките възходящо, но може да го промениш и да е по ключа - модела възходящо/низходящо.

0
02/01/2022 20:53:25
Dianov avatar Dianov 13 Точки

Благодаря много за обяснението! Значи ако разбирам правилно всеки път щом използвам .OrderBy или .OrderByDescending трябва да закача .ToDictionary() и вече в него мога да използвам още веднъж .OrderBy, последвано с .ToDictionary() докато подредя всеки вложен речник по искания критерий (ключ, стойност, брой стойности на ключ ако са от тип лист например и т.н.)? Също искам да попитам по същия начин може ли да се постигне подредба първо по критерий, засягащ вътрешния речник и да се използва .ThenBy(), в който да се включи ламбда израз, засягащ външния речник, защото се мъчих да правя нещо подобно, но постоянно ми гърми с:

System.InvalidOperationException
  HResult=0x80131509
  Message=Failed to compare two elements in the array.
  Source=System.Private.CoreLib
  StackTrace:
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource, Exception e)
   at System.Collections.Generic.ArraySortHelper`1.Sort(Span`1 keys, Comparison`1 comparer)
   at System.MemoryExtensions.Sort[T](Span`1 span, Comparison`1 comparison)
   at System.Linq.EnumerableSorter`2.QuickSort(Int32[] keys, Int32 lo, Int32 hi)
   at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__17.MoveNext()
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at Snowwhite.Program.Main(String[] args) in C:\Users\ddian\source\repos\SoftUni\CSharpFundamentals\AssociativeArraysMoreExercise\Snowwhite\Program.cs:line 54

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
ArgumentException: At least one object must implement IComparable.

 https://pastebin.com/ug4vRqGX -> ето това е целия код конкретно. Става въпрос за задачата Snowwhite от Associative Arrays - More exercises. 

When you receive the command "Once upon a time", the input ends. You must order the dwarfs by physics in descending order and then by the total count of dwarfs with the same hat color in descending order.
Then you must print them all.

При дебъг информацията си се добавя точно, както се иска в условието, но на сортировката за output-a става гърмеж и се мъча цял ден да я направя, но не се получава. 

0
k.sevov avatar k.sevov 1077 Точки

Здравей, като цяло не ти препоръчвам да ползваш .ToDictionary() за сортировки, тъй като както е казал и колегата, Dictionary не е структура, която е предвидена да държи сортирани елементи. В простите ситуации това върши работа, тъй като речниците обикновено си пазят елементите в реда на добавянето им, но по тези задачи никога няма нужда от такива неща - може директно да се сортира и печата без да ги наливаме в структура.

Реално идеята на сортирането на нестнати речници е, че си пускаш един цикъл по елементите (KeyValuePair) на външния речник (сортирани с LINQ както ти е необходимо за печатането) и след това вътре си пускаш цикъл по елементите на вътрешния речник (отново сортирани с LINQ както ти е необходимо). Колкото до въпроса дали може да се използва информация от вътрешния речник като сортираме външния - да, няма проблеми. В твоя код дава грешка, тъй като се опитваш да използваш като критерий за сравнение цялата подредена колекция от числа вместо примерно да вземеш само първото (най-голямото от тях) след като ги подредиш. 

Snowwhite задачата специално е изключение от стандартните за речници, тъй като сортирането изисква твърде много информация и само с един вложен речник няма как да се сортира независимо от сетъпа. Възможностите са или да го направим с клас както си я решил и ти (ето още един вариант с клас), или да сме по-креативни с речниците, примерно нещо такова.

0
05/01/2022 21:42:56
Dianov avatar Dianov 13 Точки

Благодаря за разяснението. Ще ми бъде много полезно в бъдеще, когато ми се налага да използвам речници.

0
Dianov avatar Dianov 13 Точки

Не се получи по начина с вложения речник. Реших я с лист от обекти, които се подредиха от първия път. Използвах отделно един речник да пази бройката на джуджета във всяка група според цвета на шапката. Преди output-a присвоих на property стойността от речника за всеки обект със същия цвят шапка, отговарящ на ключа на речника. Същото property използвах за подредбата на джуджетата в листа от джуджета.

Линк към решението, което дава 100/100 в Judge:

https://github.com/DDianovMD/SoftUni/blob/main/CSharpFundamentals/AssociativeArraysMoreExercise/Snowwhite/Program.cs
 

0
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.