2016-12-21 5 views
25

У меня есть эта структура:Как использовать LINQ, чтобы найти сумму?

private readonly Dictionary<string, Dictionary<string, int>> _storage = 
    new Dictionary<string, Dictionary<string, int>>(); 

ключ: Firmware (строка): ключ: Device (строка): значение CountOfUsers (ИНТ)

Мне нужно, чтобы получить общее количество пользователей для каждого устройства, но Я действительно не знаю, как это сделать с LINQ. Уже пробовал много вариантов. Пожалуйста помоги!

На данный момент, я просто использовать всю функция, для

private XlsRow2 GetTotalPerDevice(Dictionary<string, Dictionary<string, int>> storage) 
    { 
     XlsRow2 totalPerDeviceRow = new XlsRow2(); 
     totalPerDeviceRow._Name = "Grand Total"; 
     totalPerDeviceRow.UseBorders = true; 
     foreach (var deviceModel in _allDeviceModels) 
     { 
      foreach (var firmware in storage) 
      { 
       foreach (var device in firmware.Value) 
       { 
        var countOfUsers = 0; 
        if (deviceModel == device.Key) 
        { 
         countOfUsers += device.Value; 

         if (!_totalsPerDevice.ContainsKey(deviceModel)) 
         { 
          _totalsPerDevice.Add(deviceModel, countOfUsers); 
         } 
         else 
         { 
          _totalsPerDevice[deviceModel] += countOfUsers; 
         } 
        } 
       } 
      } 
     } 
     foreach (var deviceModel in _allDeviceModels) 
     { 
      if (_totalsPerDevice.ContainsKey(deviceModel)) 
      { 
       totalPerDeviceRow._AddColumn(_totalsPerDevice.First(k => k.Key == deviceModel.ToString()).Value.ToString()); 
      } 
      else 
      { 
       totalPerDeviceRow._AddColumn(""); 
      } 
     } 
     return totalPerDeviceRow; 
    } 

ответ

31

Что-то вроде этого, например?

var result = _storage.SelectMany(x => x.Value) 
    .GroupBy(x => x.Key) 
    .Select(x => new { Device = x.Key, Total = x.Sum(y => y.Value) }); 
11

Поскольку ключи для данных, которые вы хотели бы объединить в словаре второго уровня, хороший первый шаг должен был бы сбросить все пары ключ-значение из внутренних словарей в плоской последовательности. После этого все, что вам нужно агрегировать счетчики, например:

var res = _storage 
    .SelectMany(d => d.Value) 
    .GroupBy(kvp => kvp.Key) 
    .ToDictionary(g => g.Key, g => g.Sum(kvp => kvp.Value)); 
+0

Еще раз совет, что не всегда лучший ответ имеет самый высокий балл, а просто тот, у которого уже много голосов. Сожалею. братан. – HimBromBeere

10

Словарь реализует IEnumerable<KeyValuePair<TKey,TValue> означает, что вы можете использовать LINQ на нем. В этом случае у вас есть словарь словарей и вам нужно группировать ключ второго уровня. Чтобы сделать это, вам нужно сглаживать словари, то, что может быть сделано с SelectMany

_storage.Selectmany(pair=>pair.Value); 

После того, как у вас есть записи листа на уровне, можно сгруппировать по их ключей:

_storage.Selectmany(pair=>pair.Value) 
     .GroupBy(leaf=>leaf.Key); 

И вычислить сумма в каждой группе:

var totals=_storage.SelectMany(pair=>pair.Value) 
        .GroupBy(leaf=>leaf.Key) 
        .Select(grp=>new { 
             Device = grp.Key, 
             TotalUsers =grp.Sum(leaf=>leaf.Value) 
            }); 

Эквивалентный запрос является довольно чище:

var totals2 = from frm in _storage 
       from dev in frm.Value 
       group dev by dev.Key into grp 
       select new { 
          Device = grp.Key, 
          Total=grp.Sum(leaf=>leaf.Value) 
         }; 

Учитывая следующий словарь:

var _storage = new Dictionary<string, Dictionary<string, int>> { 
    ["Frm1"]=new Dictionary<string, int> { 
        ["Device1"]=4, 
        ["Device2"]=5 
       }, 
    ["Frm2"]=new Dictionary<string, int> { 
        ["Device1"]=41, 
        ["Device3"]=5 
       }      
}; 

Оба запроса возвращают одинаковые значения

foreach(var total in totals) 
{ 
    Console.WriteLine ($"{total.Device} = {total.Total}"); 
} 
------------------ 
Device1 = 45 
Device2 = 5 
Device3 = 5 
5

Вы можете сделать это так:

Dictionary<string, Dictionary<string, int>> _storage = new Dictionary<string, Dictionary<string, int>>(); 
Dictionary<string, int> x = new Dictionary<string, int>(); 
x.Add("x", 2); 
x.Add("z", 2); 
x.Add("y", 2); 
_storage.Add("x", x); 
_storage.Add("z", x); 
_storage.Add("y", x); 

var b = _storage.SelectMany(keyValuePair => keyValuePair.Value) 
     .GroupBy(keyValuePair => keyValuePair.Key) 
     .ToDictionary(valuePairs => valuePairs.Key, grouping => grouping.Sum(kvp => kvp.Value)); 

результат будет как: enter image description here

Смежные вопросы