HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Removing exact instances of elements in one list from another

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
exactelementsinstancesremovingoneanotherlistfrom

Problem

Basically, here's the problem statement:


Given an IEnumerable source and an IEnumerable exceptions, return an IEnumerable 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 ExceptExact(this IEnumerable source, IEnumerable exceptions)
{
    var tExceptions = new List();
    tExceptions.AddRange(exceptions);

    var result = new List();

    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 { 2, 3, 5, 4, 2 };
var b = new List { 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 }

Solution

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 ExceptExact(this IEnumerable source, IEnumerable exceptions)
{
    var tExceptions = exceptions.ToList();

    foreach (var el in source)
    {
        var index = tExceptions.IndexOf(el);
        if (index >= 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 ExceptExact(this IEnumerable source, IEnumerable exceptions)
{
    var tExceptions = exceptions.ToList();
    return source.Where(el => !tExceptions.Remove(el));
}

Code Snippets

public static IEnumerable<T> ExceptExact<T>(this IEnumerable<T> source, IEnumerable<T> exceptions)
{
    var tExceptions = exceptions.ToList();

    foreach (var el in source)
    {
        var index = tExceptions.IndexOf(el);
        if (index >= 0)
        {
            tExceptions.RemoveAt(index);
            continue;
        }
        yield return el;
    }
}
public static IEnumerable<T> ExceptExact<T>(this IEnumerable<T> source, IEnumerable<T> exceptions)
{
    var tExceptions = exceptions.ToList();
    return source.Where(el => !tExceptions.Remove(el));
}

Context

StackExchange Code Review Q#134413, answer score: 8

Revisions (0)

No revisions yet.