Removing exact instances of elements in one list from another Basically, here's the problem statement:


  Given an IEnumerable&lt;T&gt; source and an IEnumerable&lt;T&gt; exceptions, return an IEnumerable&lt;T&gt; result which contains all of source, without exceptions, only omitting exact instances of a value. I.e.: if a value appears twice in source, but once in exceptions, then result will have that value exactly one time.


Let's take an example:

Exception list:

{ 2, 2, 3, 4, 5}


Source list:

{ 2, 2, 2, 6 }


Result:

{ 2, 6 }




Code:

public static IEnumerable&lt;T&gt; ExceptExact&lt;T&gt;(this IEnumerable&lt;T&gt; source, IEnumerable&lt;T&gt; exceptions)
{
    var tExceptions = new List&lt;T&gt;();
    tExceptions.AddRange(exceptions);

    var result = new List&lt;T&gt;();

    foreach (var el in source)
    {
        if (tExceptions.Contains(el))
        {
            tExceptions.RemoveAt(tExceptions.IndexOf(el));
        }
        else
        {
            result.Add(el);
        }
    }

    return result;
}




Usage:


var a = new List&lt;int&gt; { 2, 3, 5, 4, 2 };
var b = new List&lt;int&gt; { 2, 2, 2, 6 };

var c = b.ExceptExact(a);
var d = a.ExceptExact(b);

Console.WriteLine("A: { " + string.Join(", ", a) + " }");
Console.WriteLine("B: { " + string.Join(", ", b) + " }");
Console.WriteLine("C: { " + string.Join(", ", c) + " }");
Console.WriteLine("D: { " + string.Join(", ", d) + " }");



Output:


A: { 2, 3, 5, 4, 2 }
B: { 2, 2, 2, 6 }
C: { 2, 6 }
D: { 3, 5, 4 }
Additional to the valid point from Rick Davin:

1) You can drop the Contains because IndexOf returns -1 if the item is not contained.

2) You could use yield to get rid of the additional list 



public static IEnumerable&lt;T&gt; ExceptExact&lt;T&gt;(this IEnumerable&lt;T&gt; source, IEnumerable&lt;T&gt; exceptions)
{
    var tExceptions = exceptions.ToList();

    foreach (var el in source)
    {
        var index = tExceptions.IndexOf(el);
        if (index &gt;= 0)
        {
            tExceptions.RemoveAt(index);
            continue;
        }
        yield return el;
    }
}




After taking a look to MSDN (List.Remove), I figured out that there is even a simpler way:


  Removes the first occurrence of a specific object from the List.
  
  Returns true if item is successfully removed; otherwise, false.This method
  also returns false if item was not found in the List.


public static IEnumerable&lt;T&gt; ExceptExact&lt;T&gt;(this IEnumerable&lt;T&gt; source, IEnumerable&lt;T&gt; exceptions)
{
    var tExceptions = exceptions.ToList();
    return source.Where(el =&gt; !tExceptions.Remove(el));
}