I'm looking for an extension method or any other suggestion that can help me make this code as concise as possible.
foreach( Layer lyr in this.ProgramLayers )
foreach( UWBCEvent evt in this.BcEvents.IncludedEvents )
EventGroupLayerLosses[new EventGroupIDLayerTuple(evt.EventGroupID, lyr)] +=
GetEL(evt.AsIfs, lyr.LimitInMillions, lyr.AttachmentInMillions);
The above code has a fairly clear purpose, I'm bucketing values into groups with a compound key. However, this code will fail because the dictionary is initially empty and the += operator won't know to start the bucket off at 0.
The best I can come up with is this:
public V AddOrSet<K, V>(this Dictionary<K, V> dict, K key, V value)
{
if( dict.ContainsKey(key) )
dict[key] += value;
else
dict[key] = value;
}
But of course, even that won't compile because there's no way to restrict the type of V such that the operator += exists.
Rules
- Only one iteration through the double for loop. Not allowed to loop through once before to initialize the dictionary with 0 values.
- Helper method or extension method can be used, but I want the inner loop to be a one liner.
- Be as generic and reusable as possible so that I don't need to create a bunch of identical functions for similar bucketing with different types (decimals, ints, etc).
For reference - elsewhere in the class the key is defined as an actual Tuple (just with named parameters), which is why it can be used as a dictionary key:
private Dictionary<EventGroupIDLayerTuple, Decimal> _EventGroupLayerLosses;
public class EventGroupIDLayerTuple : Tuple<Int32, Layer>
{
public EventGroupIDLayerTuple(Int32 EventGroupID, Layer Layer) : base(EventGroupID, Layer) { }
public Int32 EventGroupID { get { return this.Item1; } }
public Layer Layer { get { return this.Item2; } }
}
Solution
Thanks to Jon Skeet for the idea of passing a Lambda function as a third parameter to my extension method. No need to even restrict it to a += operation anymore. It's generic enough any operation can be passed to set the new value if a value already exists.
//Sets dictionary value using the provided value. If a value already exists,
//uses the lambda function provided to compute the new value.
public static void UpdateOrSet<K, V>(this Dictionary<K, V> dict, K key, V value, Func<V, V, V> operation)
{
V currentValue;
if( dict.TryGetValue(key, out currentValue) )
dict[key] = operation(currentValue, value);
else
dict[key] = value;
}
Examples:
mySums.UpdateOrSet("Bucket1", 12, (x, y) => x + y);
myStrs.UpdateOrSet("Animals", "Dog", (x, y) => x + ", " + y);
myLists.UpdateOrSet("Animals", (List<T>) Dogs, (x, y) => x.AddRange(y));
Endless fun!