Reputation: 1389
I need to get the set subtraction of two string arrays while considering duplicates.
Ex:
var a = new string[] {"1", "2", "2", "3", "4", "4"};
var b = new string[] {"2", "3"};
(a - b) => expected output => string[] {"1", "2", "4", "4"}
I already tried Enumerable.Except()
which returns the unique values after subtract: { "1", "4" }
which is not what I'm looking for.
Is there a straightforward way of achieving this without a custom implementation?
Upvotes: 0
Views: 876
Reputation: 120410
By leveraging the undersung Enumerable.ToLookup
(which allows you to create dictionary-like structure with multi-values per key) you can do this quite efficiently. Here, because key lookups on non-existent keys in an ILookup
return empty IGrouping
(rather than null
or an error), you can avoid a whole bunch of null-checks/TryGet...
-boilerplate. Because Enumerable.Take
with a negative value is equivalent to Enumerable.Take(0)
, we don't have to check our arithmetic either.
var aLookup = a.ToLookup(x => x);
var bLookup = b.ToLookup(x => x);
var filtered = aLookup
.SelectMany(aItem => aItem.Take(aItem.Count() - bLookup[aItem.Key].Count()));
Upvotes: 3
Reputation: 5550
Try the following:
var a = new string[] { "1", "2", "2", "3", "4", "4" }.ToList();
var b = new string[] { "2", "3" };
foreach (var element in b)
{
a.Remove(element);
}
Has been tested.
Upvotes: 2
Reputation: 186668
You can try GroupBy
, and work with groups e.g.
var a = new string[] {"1", "2", "2", "3", "4", "4"};
var b = new string[] {"2", "3"};
...
var subtract = b
.GroupBy(item => item)
.ToDictionary(chunk => chunk.Key, chunk => chunk.Count());
var result = a
.GroupBy(item => item)
.Select(chunk => new {
value = chunk.Key,
count = chunk.Count() - (subtract.TryGetValue(chunk.Key, out var v) ? v : 0)
})
.Where(item => item.count > 0)
.SelectMany(item => Enumerable.Repeat(item.value, item.count));
// Let's have a look at the result
Console.Write(string.Join(", ", result));
Outcome:
1, 2, 4, 4
Upvotes: 3