Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way to create a nested Dictionary in C#

Tags:

I realized that I didn't give enough information for most people to read my mind and understand all my needs, so I changed this somewhat from the original.

Say I've got a list of items of a class like this:

public class Thing {     int Foo;     int Bar;     string Baz; } 

And I want to categorize the Baz string based on the values of Foo, then Bar. There will be at most one Thing for each possible combination of Foo and Bar values, but I'm not guaranteed to have a value for each one. It may help to conceptualize it as cell information for a table: Foo is the row number, Bar is the column number, and Baz is the value to be found there, but there won't necessarily be a value present for every cell.

IEnumerable<Thing> things = GetThings(); List<int> foos = GetAllFoos(); List<int> bars = GetAllBars(); Dictionary<int, Dictionary<int, string>> dict = // what do I put here? foreach(int foo in foos) {     // I may have code here to do something for each foo...     foreach(int bar in bars)     {         // I may have code here to do something for each bar...         if (dict.ContainsKey(foo) && dict[foo].ContainsKey(bar))         {             // I want to have O(1) lookups             string baz = dict[foo][bar];             // I may have code here to do something with the baz.         }     } } 

What's an easy, elegant way to generate the nested dictionary? I've been using C# long enough that I'm getting used to finding simple, one-line solutions for all of the common stuff like this, but this one has me stumped.

like image 256
StriplingWarrior Avatar asked Dec 17 '09 00:12

StriplingWarrior


People also ask

Is it possible to make nested dictionary?

You can create a nested dictionary in Python by placing comma-separated dictionaries within curly braces {}. A Python nested dictionary allows you to store and access data using the key-value mapping structure within an existing dictionary.

How are the nested dictionaries created?

Creating a Nested Dictionary In Python, a Nested dictionary can be created by placing the comma-separated dictionaries enclosed within braces.

Are nested dictionaries bad practice?

There is nothing inherently wrong with nested dicts. Anything can be a dict value, and it can make sense for a dict to be one. A lot of the time when people make nested dicts, their problems could be solved slightly more easily by using a dict with tuples for keys.


2 Answers

Here's a solution using Linq:

Dictionary<int, Dictionary<int, string>> dict = things     .GroupBy(thing => thing.Foo)     .ToDictionary(fooGroup => fooGroup.Key,                   fooGroup => fooGroup.ToDictionary(thing => thing.Bar,                                                     thing => thing.Baz)); 
like image 193
Mark Byers Avatar answered Oct 20 '22 06:10

Mark Byers


An elegant way would be to not create the dictionaries yourself but use LINQ GroupBy and ToDictionary to generate it for you.

var things = new[] {     new Thing { Foo = 1, Bar = 2, Baz = "ONETWO!" },     new Thing { Foo = 1, Bar = 3, Baz = "ONETHREE!" },     new Thing { Foo = 1, Bar = 2, Baz = "ONETWO!" } }.ToList();  var bazGroups = things     .GroupBy(t => t.Foo)     .ToDictionary(gFoo => gFoo.Key, gFoo => gFoo         .GroupBy(t => t.Bar)         .ToDictionary(gBar => gBar.Key, gBar => gBar.First().Baz));  Debug.Fail("Inspect the bazGroups variable."); 

I assume that by categorizing Baz using Foo and Bar you mean that if two things have both Foo and Bar equals then their Baz value also be the same as well. Please correct me if I'm wrong.

You're basically group by the Foo property first...
then for each resulting group, you group on the Bar property...
then for each resulting group you take the first Baz value as the dictionary value.

If you noticed, the method names matched exactly what you are trying to do. :-)


EDIT: Here's another way using query comprehensions, they are longer but are quiet easier to read and grok:

var bazGroups =     (from t1 in things      group t1 by t1.Foo into gFoo      select new      {          Key = gFoo.Key,          Value = (from t2 in gFoo                   group t2 by t2.Bar into gBar                   select gBar)                   .ToDictionary(g => g.Key, g => g.First().Baz)      })      .ToDictionary(g => g.Key, g => g.Value); 

Unfortunately, there are no query comprehension counterpart for ToDictionary so it's not as elegant as the lambda expressions.

...

Hope this helps.

like image 27
chakrit Avatar answered Oct 20 '22 05:10

chakrit