Skip to content

Instantly share code, notes, and snippets.

@appelgran
Last active November 14, 2019 11:02
Show Gist options
  • Save appelgran/64671fceaed510fe17206a05e877073c to your computer and use it in GitHub Desktop.
Save appelgran/64671fceaed510fe17206a05e877073c to your computer and use it in GitHub Desktop.
Linq Except Indistinct
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// Credit to Alex Siepman and digEmAll on StackOverflow (answer https://stackoverflow.com/a/18321594/1049710)
// Demonstration
var newValues = new string[] { "A", "C", "D", "D", "E", "F", "F", "G", "G" };
var oldValues = new string[] { "A", "B", "C", "E", "E", "G" };
Console.WriteLine("Except in new but not in old: " + String.Join(",", newValues.Except(oldValues)));
// output: D,F
Console.WriteLine("Except in old but not in new: " + String.Join(",", oldValues.Except(newValues)));
// output: B
Console.WriteLine("ExceptIndistinct in new but not in old: " + String.Join(",", newValues.ExceptIndistinct(oldValues)));
// output: D,D,F,F,G
Console.WriteLine("ExceptIndistinct in old but not in new: " + String.Join(",", oldValues.ExceptIndistinct(newValues)));
// output: B,E
}
}
public static class ExceptIndistinctExtensions
{
/// <summary>Returns a list of all values except those also found in given second list.</summary>
public static IEnumerable<TSource> ExceptIndistinct<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
return ExceptIndistinct(first, second, null);
}
/// <summary>Returns a list of all values except those also found in given second list.</summary>
public static IEnumerable<TSource> ExceptIndistinct<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer)
{
if (first == null) { throw new ArgumentNullException("first"); }
if (second == null) { throw new ArgumentNullException("second"); }
var secondCounts = new Dictionary<TSource, int>(comparer ?? EqualityComparer<TSource>.Default);
int count;
int nullCount = 0;
// Count the values from second
foreach (var item in second)
{
if (item == null)
{
nullCount++;
}
else
{
if (secondCounts.TryGetValue(item, out count))
{
secondCounts[item] = count + 1;
}
else
{
secondCounts.Add(item, 1);
}
}
}
// Yield the values from first
foreach (var item in first)
{
if (item == null)
{
nullCount--;
if (nullCount < 0)
{
yield return item;
}
}
else
{
if (secondCounts.TryGetValue(item, out count))
{
if (count == 0)
{
secondCounts.Remove(item);
yield return item;
}
else
{
secondCounts[item] = count - 1;
}
}
else
{
yield return item;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment