在C#中创建嵌套字典的优雅方式

Str*_*ior 17 c# linq

我意识到我没有给大多数人提供足够的信息来阅读我的想法并了解我的所有需求,所以我对此进行了一些改动.

假设我有一个像这样的类的项目列表:

public class Thing
{
    int Foo;
    int Bar;
    string Baz;
}
Run Code Online (Sandbox Code Playgroud)

我想根据Foo的值对Baz字符串进行分类,然后是Bar.对于Foo和Bar值的每种可能组合,最多只有一个Thing,但我不保证每个值都有一个值.将它概念化为表的单元信息可能有所帮助:Foo是行号,Bar是列号,Baz是在那里找到的值,但不一定存在每个单元的值.

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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

生成嵌套字典的简单,优雅的方法是什么?我一直在使用C#,以至于我已经习惯于为所有常见的东西找到简单的单行解决方案,但是这个让我很难过.

Mar*_*ers 30

这是使用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));
Run Code Online (Sandbox Code Playgroud)

  • 使用"var dict ="你可以使用LINQ来折叠多个foreach语句:var bazs = dict.SelectMany(topPair => topPair.Value.Values); foreach(bazs中的字符串baz){// ...} (3认同)

cha*_*rit 20

一种优雅的方式是自己创建字典,但使用LINQ GroupByToDictionary为您生成它.

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.");
Run Code Online (Sandbox Code Playgroud)

我假设通过分类Baz使用Foo,Bar你的意思是如果两个东西都有,Foo并且Bar等于那么它们的Baz值也是相同的.如果我错了,请纠正我.

您基本上Foo首先按属性分组...
然后对于每个结果组,您在Bar属性上进行分组...
然后对于每个结果组,您将第一个Baz值作为字典值.

如果您注意到,方法名称与您尝试的完全匹配.:-)


编辑:这是使用查询理解的另一种方式,它们更长但更安静,更容易阅读和理解:

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);
Run Code Online (Sandbox Code Playgroud)

不幸的是,ToDictionary没有查询理解对应物,所以它不如lambda表达式那么优雅.

...

希望这可以帮助.


Cha*_*ana 5

定义您自己的自定义泛型NestedDictionary

public class NestedDictionary<K1, K2, V>: 
     Dictionary<K1, Dictionary<K2, V>> {}
Run Code Online (Sandbox Code Playgroud)

然后在你写的代码中

NestedDictionary<int, int, string> dict = 
       new NestedDictionary<int, int, string> ();
Run Code Online (Sandbox Code Playgroud)

如果您经常使用 int、int、string one,也请为此定义一个自定义类。

   public class NestedIntStringDictionary: 
        NestedDictionary<int, int, string> {}
Run Code Online (Sandbox Code Playgroud)

然后写:

  NestedIntStringDictionary dict = 
          new NestedIntStringDictionary();
Run Code Online (Sandbox Code Playgroud)

编辑:添加从提供的项目列表构建特定实例的功能:

   public class NestedIntStringDictionary: 
        NestedDictionary<int, int, string> 
   {
        public NestedIntStringDictionary(IEnumerable<> items)
        {
            foreach(Thing t in items)
            {
                Dictionary<int, string> innrDict = 
                       ContainsKey(t.Foo)? this[t.Foo]: 
                           new Dictionary<int, string> (); 
                if (innrDict.ContainsKey(t.Bar))
                   throw new ArgumentException(
                        string.Format(
                          "key value: {0} is already in dictionary", t.Bar));
                else innrDict.Add(t.Bar, t.Baz);
            }
        }
   }
Run Code Online (Sandbox Code Playgroud)

然后写:

  NestedIntStringDictionary dict = 
       new NestedIntStringDictionary(GetThings());
Run Code Online (Sandbox Code Playgroud)