遍历嵌套字典

时间:2020-03-24 15:11:41

标签: c# dictionary nested iterator

我正在尝试遍历以下嵌套的字典实例:

Dictionary<String, Dictionary<Datetime, int>> nestedDictionary =
    new Dictionary<String, Dictionary<Datetime, int>>()

目标是遍历字典中的所有整数。 要遍历字典实例,我使用嵌套的foreach循环,如下所示:

int highestTargetInConstraint = 0;
foreach(String key1 in nestedDictionary.Keys) 
{
    foreach(int targetValue in nestedDictionary[key1].Values) 
    {
        if(targetValue > 5) { continue; }

        if(targetValue > highestTargetInConstraint)
        {
            highestTargetInConstraint = targetValue;
        }
    }
}

这是this post中提到的遍历平面字典的最合适方法。我无法想象会比这更快,但是也许您可以反驳。

现在,我想知道是否存在一种使用自定义迭代器的语法更好的循环嵌套字典的方法。感谢有用的提示和指向相关文档的指针。

2 个答案:

答案 0 :(得分:2)

直接遍历值比遍历键然后进行额外的查找(如您的示例代码中)要有效。

这样更好:

foreach (var dictionary in nestedDictionary.Values)
{
    foreach (int targetValue in dictionary.Values)
    {
        // Do something with targetValue
    }
}

这里有一些测试代码可以将速度与OP进行比较:

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace FooBar
{
    class Program
    {
        static void Main(string[] args)
        {
            var nestedDictionary = new Dictionary<String, Dictionary<DateTime, int>>();

            int numInnerDictionaries = 5000;
            int numInnerInts         = 5000;

            for (int i = 0; i < numInnerDictionaries; ++i)
            {
                var innerDict = new Dictionary<DateTime, int>();

                var start = new DateTime(2020, 01, 01);

                for (int j = 0; j < numInnerInts; ++j)
                    innerDict.Add(start.AddSeconds(j), j);

                nestedDictionary.Add(i.ToString(), innerDict);
            }

            var sw = new Stopwatch();

            for (int trial = 0; trial < 5; ++trial)
            {
                sw.Restart();
                method1(nestedDictionary);
                sw.Stop();
                Console.WriteLine("method1() took " + sw.Elapsed);
                double method1Ticks = sw.ElapsedTicks;

                sw.Restart();
                method2(nestedDictionary);
                sw.Stop();
                Console.WriteLine("method2() took " + sw.Elapsed);
                Console.WriteLine($"method1() was {method1Ticks/sw.ElapsedTicks} times faster.");

                Console.WriteLine();
            }
        }

        static long method1(Dictionary<String, Dictionary<DateTime, int>> nestedDictionary)
        {
            long total = 0;

            foreach (var dictionary in nestedDictionary)
            {
                foreach (var item in dictionary.Value)
                {
                    total += item.Value;
                }
            }

            return total;
        }

        static long method2(Dictionary<String, Dictionary<DateTime, int>> nestedDictionary)
        {
            long result = 0;

            foreach(String key1 in nestedDictionary.Keys)
            {
                foreach(int targetValue in nestedDictionary[key1].Values)
                {
                    result += targetValue;
                }
            }

            return result;
        }
    }
}

当我运行RELEASE(不是DEBUG)构建时,它报告说改进的方法比原始方法快3倍以上。 3倍加速还算不错...

当然,要加快多少速度取决于有多少个外部词典-数量越多,加速程度就越大。

例如,如果我将numInnerDictionaries更改为50,则提速只有大约两倍。

答案 1 :(得分:0)

它在LINQ中。只需一行即可实际完成-剩下的就是设置和显示。

// Set up some data
var in1 = new Dictionary<DateTime, int> { { DateTime.Now, 1 }, { DateTime.Now, 2 }, { DateTime.Now, 3 } };
var in2 = new Dictionary<DateTime, int> { { DateTime.Now, 11 }, { DateTime.Now, 12 }, { DateTime.Now, 13 } };
var in3 = new Dictionary<DateTime, int> { { DateTime.Now, 21 }, { DateTime.Now, 22 }, { DateTime.Now, 23 } };

Dictionary<string, Dictionary<DateTime, int>> nestedDictionary = new Dictionary<string, Dictionary<DateTime, int>>
{
    { "A",in1 },
    { "B",in2 },
    { "C",in3 }
};

// Do the query
 IEnumerable<int> ints = nestedDictionary.SelectMany(n => n.Value.Select(s => s.Value));

// Show the results
foreach (int i in ints)
    Console.WriteLine(i);