1

I'm using Enumerable.Except() to exclude skipSerialNumbers items from activatedSerialNumbers.

activatedSerialNumbers = activatedSerialNumbers
                    .Except(skipSerialNumbers, new SamWithLicenseComparer()).ToList();

SamWithLicenseComparer is:

internal class SamWithLicenseComparer : IEqualityComparer<SamWithLicense>
{
    public bool Equals(SamWithLicense x, SamWithLicense y)
    {
        if (ReferenceEquals(x, y)) 
            return true;

        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;

        if(x.Name.ToLower() != y.Name.ToLower())
            return false;
        return true;
    }

    public int GetHashCode(SamWithLicense sam)
    {
        if (ReferenceEquals(sam, null)) 
            return 0;
        return sam.Name == null ? 0 : sam.Name.ToLower().GetHashCode();
    }
}

As a result I get unexpected value, because, as I found out, items from activatedSerialNumbers compare with themselves. But the logic is that they may have the same names, task is just remove all items from skipSerialNumbers. How to do that avoiding an extra comparison?

Zharro
  • 819
  • 1
  • 11
  • 23
  • 4
    I think not all code paths return value in your Equals method. Are you missing `return true` at the end? – Sergey Berezovskiy Aug 14 '13 at 16:31
  • 3
    If you want to compare ignoring upper and lower case, there are specific methods to do it. For example `string.Compare(x, y, true)` or even better `string.Compare(x, y, StringComparison.CurrentCultureIgnoreCase)` so that everyone that sees it knows what is happening (and can begin pestering you on why `CurrentCulture`). Then clearly the GetHashCode should use: `StringComparer.CurrentCultureIgnoreCase.GetHashCode(sam)` – xanatos Aug 14 '13 at 16:37
  • @lazyberezovsky thank you, I just missed this code. – Zharro Aug 14 '13 at 16:47

2 Answers2

3

Except is a set operation so the result will contain always distinct values.
For example if you do {A,B,A,C}.Except({B,C}) you'll get {A}, not {A, A}.

You can try this:

var skipSesialNumbersSet = new HashSet<SamWithLicense>(skipSerialNumbers, new SamWithLicenseComparer());
activatedSerialNumbers = activatedSerialNumbers.Where(x => !skipSesialNumbersSet.Contains(x)).ToList();
digEmAll
  • 56,430
  • 9
  • 115
  • 140
  • 1
    you need to actually add the items to be removed to the hashset, but other than that, this is right – Servy Aug 14 '13 at 16:53
  • Using a HashSet for skipSesialNumbersSet will still result in a distinct of the second collection. For example 1, 1, 3, 4 except 1, 3 will result in 4 and not result in 1, 4. So there is still a distict of 1 being done. – Alex Siepman Aug 19 '13 at 12:00
  • @AlexSiepman: well `{1,1,3,4}` except `{1,3}` has to be `{4}`... I don't think the OP wants to remove specific sub-sequences, but all the occurrences of certain values... – digEmAll Aug 19 '13 at 14:59
0

If you want to see the duplicates also, you can use this variant of Except:

public static IEnumerable<TSource> ExceptWithDuplicates<TSource>(
        this IEnumerable<TSource> first,
        IEnumerable<TSource> second)
    {
        if (first == null) { throw new ArgumentNullException("first"); }
        if (second == null) { throw new ArgumentNullException("second"); }

        var secondList = second.ToList();
        return first.Where(s => !secondList.Remove(s));
    }

    public static IEnumerable<TSource> ExceptWithDuplicates<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 comparerUsed = comparer ?? EqualityComparer<TSource>.Default;

        var secondList = second.ToList();
        foreach (var item in first)
        {
            if (secondList.Contains(item, comparerUsed))
            {
                secondList.Remove(item);
            }
            else
            {
                yield return item;
            }
        }
    }

Now you can do this:

var first = new [] { 1, 1, 1, 2, 2, 2, 3, 3 };
var second = new[] { 1, 2, 2, 4 };

var withoutDuplicates = first.Except(second);  // 3 
var witDuplicates = first.ExceptWithDuplicates(second);  // 1, 1, 2, 3, 3 : Note that 1 and 2 are also in the list
Alex Siepman
  • 2,499
  • 23
  • 31