问题
I have a loop which, once simplified, looks like this:
Dictionary<Tuple<A,G>,Decimal> results = new Dictionary<Tuple<A,G>,Decimal>();
foreach( A a in collectionA )
foreach( B b in collectionB )
results [Tuple.Create(a, (G)b.groupBy)] += (Decimal) Func(a, b);
Is there a way I can replicate this result using a Linq query (with GroupBy, Sum and ToDictionary for example)? (as suggested in this answer to a previous question: Concise way to do a plus equals operation on a Dictionary element that might not be initialized)
Result
//Dictionary<EventGroupIDLayerTuple, Decimal> BcEventGroupLayerLosses
Using Yuxiu Li's answer below, I was able to convert this 4 liner from the linked question:
BcEventGroupLayerLosses = new Dictionary<EventGroupIDLayerTuple, Decimal>();
foreach( UWBCEvent evt in this.BcEvents.IncludedEvents )
foreach( Layer lyr in this.ProgramLayers )
BcEventGroupLayerLosses.AddOrUpdate(
new EventGroupIDLayerTuple(evt.EventGroupID, lyr),
GetEL(evt.AsIfs, lyr.LimitInMillions, lyr.AttachmentInMillions),
(a, b) => a + b);
into this one liner:
BcEventGroupLayerLosses = this.BcEvents.IncludedEvents
.SelectMany(evt => ProgramLayers, (evt, lyr) => new { evt, lyr })
.GroupBy(g => new EventGroupIDLayerTuple(g.evt.EventGroupID, g.lyr),
g => GetEL(g.evt.AsIfs, g.lyr.LimitInMillions, g.lyr.AttachmentInMillions))
.ToDictionary(g => g.Key, g => g.Sum());
And both yielded identical results.
Granted neither is particularly readable, this was a good experiment. Thanks everyone for your help!
回答1:
Dictionary<Tuple<A, G>, decimal> dictionary =
(from a in collectionA
from b in collectionB
group (decimal)Func(a, b) by Tuple.Create<A, G>(a, b.groupBy))
.ToDictionary(g => g.Key, g => g.Sum());
In declarative synatax
var dictionary = collectionA
.SelectMany(a => collectionB,
(a, b) => new { a, b })
.GroupBy(g => Tuple.Create(g.a, g.b.groupBy),
g => Func(g.a, g.b))
.ToDictionary(g => g.Key, g => g.Sum());
回答2:
I suspect you want something along the lines of:
// Extract the key/value pair from the nested loop
var result = collectionA.SelectMany(a => collectionB,
(a, b) => new {
Key = Tuple.Create(a, (G)b.groupBy),
Value = (decimal) Func(a, b)
})
// Group by the key, and convert each group's values
// to its sum
.GroupBy(pair => pair.Key,
pair => pair.Value,
(key, values) => new { Key = key,
Value = values.Sum() })
// Make a dictionary from the key/value pairs
.ToDictionary(pair => pair.Key, pair => pair.Value);
This is off the top of my head, and may need some more brackets :) I don't have time to add an explanation yet, but can do so later.
来源:https://stackoverflow.com/questions/11906103/convert-double-for-loop-into-a-linq-query