开发者

Aggregate columns in a table represented as List<Dictionary<ColumnT, ValueT>>

What is a LINQ way of aggregating column values in a table, where each row is a Dictionary<ColumnT, ValueT>?

The data table is of form

   2010 | 2011
A    11 |   12
B    21 |   22

This is represented as a list of dictionaries:

var A = new Dictionary<String, int>();
A.Add("2010", 11);
A.Add("2011"  12),
var B = new Dictionary<String, int>();
A.Add("2010", 21);
A.Add("2011"  22),
var table = List<Dictionary<String,int>>() 开发者_运维问答{ A, B };

How would one produce a totals row that aggregates all columns from other rows? The totals row would also be a Dictionary<String, int> like the other rows.

The old style way is:

var totalsRow = new Dictionary<String, int>();
foreach(Dictionary<String, int> row in table)
{
    foreach(var cell in row)
    {
       // Get running sum (add if not present)
       int TValue= 0;
       if (!totalsRow.TryGetValue(cell.Key, out cellValue))
       {
          totalsRow.Add(cell.Key, 0);
       }

       // Increment using an aggregation function (e.g. sum)
       totalsRow[cell.Key] = AggregationFunc(totalsRow[cell.Key], cellValue);
    }
}

Is there a more succinct, LINQ way of doing this?

If not, what is the most LINQ-like way to define an extension method (on Dictionary) to wrap the above code?

Clarification: AggregationFunc is any function that takes the running total and next item value and produces the new running total, e.g.

int Sum(int sum, int nextValue) { return sum + nextValue; }


I assume your last line is actually:

totalsRow[cell.Key] = AggregationFunc(runningValue, cell.Value);

because otherwise, you're not using the integer-values of the embedded dictionaries anywhere.


You could do:

var totalsRow = table.SelectMany(row => row)
                     .GroupBy(kvp => kvp.Key)
                     .ToDictionary(group => group.Key, 
                                   group => group.Sum(kvp => kvp.Value));

If you want to keep the AggregationFunc, replace the second argument to ToDictionary with:

group => group.Aggregate(0, 
         (runningValue, nextKvp) => AggregationFunc(runningValue, nextKvp.Value))

The idea is to:

  1. First flatten the list of dictionaries into a sequence of key-value pairs.
  2. Group the key-value pairs by key.
  3. Turn the groups of key-value pairs into a dictionary with:
    • The "key" being the group's key, i.e. the key shared by all the pairs in the group.
    • The "value" being the aggregation of the individual values of all the pairs in the group.


Here it is:

var totals = table
    .SelectMany(_ => _)
    .GroupBy(_ => _.Key, _ => _.Value)
    .ToDictionary(
        group => group.Key,
        group => group.Aggregate(AggregationFunc));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜