Question

I have two collections that I want to intersect, and perform a sum operation on matching elements.

For example the collections are (in pseudo code):

col1 = { {"A", 5}, {"B", 3}, {"C", 2} }
col2 = { {"B", 1}, {"C", 8}, {"D", 6} }

and the desired result is:

intersection = { {"B", 4}, {"C", 10} }

I know how to use an IEqualityComparer to match the elements on their name, but how to sum the values while doing the intersection?

EDIT:

The starting collections haven't two items with the same name.

Was it helpful?

Solution

Let's say your input data looks like this:

IEnumerable<Tuple<string, int>> firstSequence = ..., secondSequence = ...;

If the strings are unique in each sequence (i.e there can be no more than a single {"A", XXX} in either sequence) you can join like this:

var query = from tuple1 in firstSequence
            join tuple2 in secondSequence on tuple1.Item1 equals tuple2.Item1
            select Tuple.Create(tuple1.Item1, tuple1.Item2 + tuple2.Item2);

You might also want to consider using a group by, which would be more appropriate if this uniqueness doesn't hold:

var query = from tuple in firstSequence.Concat(secondSequence)
            group tuple.Item2 by tuple.Item1 into g
            select Tuple.Create(g.Key, g.Sum());

If neither is what you want, please clarify your requirements more precisely.

EDIT: After your clarification that these are dictionaries - your existing solution is perfectly fine. Here's another alternative with join:

var joined = from kvp1 in dict1
             join kvp2 in dict2 on kvp1.Key equals kvp2.Key
             select new { kvp1.Key, Value = kvp1.Value + kvp2.Value };

var result = joined.ToDictionary(t => t.Key, t => t.Value);

or in fluent syntax:

var result = dict1.Join(dict2,
                        kvp => kvp.Key,
                        kvp => kvp.Key,
                        (kvp1, kvp2) => new { kvp1.Key, Value = kvp1.Value + kvp2.Value })
                  .ToDictionary(a => a.Key, a => a.Value);

OTHER TIPS

This will give the result, but there are some caveats. It does an union of the two collections and then it groups them by letter. So if, for example, col1 contained two A elements, it would sum them together and, because now they are 2 A, it would return them.

var col1 = new[] { new { L = "A", N = 5 }, new { L = "B", N = 3 }, new { L = "C", N = 2 } };
var col2 = new[] { new { L = "B", N = 1 }, new { L = "C", N = 8 }, new { L = "D", N = 6 } };

var res = col1.Concat(col2)
              .GroupBy(p => p.L)
              .Where(p => p.Count() > 1)
              .Select(p => new { L = p.Key, N = p.Sum(q => q.N) })
              .ToArray();

The best I came up with until now is (my collections are actually Dictionary<string, int> instances):

var intersectingKeys = col1.Keys.Intersect(col2.Keys);
var intersection = intersectingKeys
    .ToDictionary(key => key, key => col1[key] + col2[key]);

I'm not sure if it will perform well, at least is it readable.

If your intersection algorithm will result in anonymous type, i.e. ...Select(new { Key = key, Value = value}) then you can easily sum it

result.Sum(e => e.Value);

If you want to sum the "while" doing the intersection, add the value to the accumulator value when adding to the result set.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top