Wijitha
Wijitha

Reputation: 1389

Set subtraction while keeping duplicates

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

Answers (3)

spender
spender

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

Peter Smith
Peter Smith

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

Dmitrii Bychenko
Dmitrii Bychenko

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

Related Questions