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:
- First flatten the list of dictionaries into a sequence of key-value pairs.
- Group the key-value pairs by key.
- 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));
精彩评论