Итерация по вложенному словарю - PullRequest
0 голосов
/ 24 марта 2020

Я пытаюсь перебрать следующий вложенный экземпляр словаря:

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

Цель состоит в том, чтобы перебрать все целые числа в словаре. Чтобы перебрать экземпляр словаря, я использую вложенный foreach l oop, как показано ниже:

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

Это наиболее подходящий способ для l oop через плоский словарь, как упомянуто в этот пост . Я не могу представить, что получу быстрее, чем это, но, может быть, вы можете опровергнуть.

Теперь мне интересно, есть ли синтаксически лучший способ для l oop через вложенный словарь с помощью пользовательского итератора. Полезные советы и ссылки на соответствующие документы приветствуются.

Ответы [ 2 ]

2 голосов
/ 24 марта 2020

Более эффективно выполнять итерацию по значениям напрямую, а не по итерации ключей, а затем выполнять дополнительный поиск (как в примере кода).

Так что это лучше:

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, ускорение будет примерно вдвое быстрее.

0 голосов
/ 24 марта 2020

Вот это в 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);
...