3

I got trouble getting the difference between 2 list in c#.

How can i get the difference ? I have tried Except but I did not get the result i wanted.

for example: These products are part of a bill

class Product {
   public int id_prod;
   public int quantity;
   public float price;
}

Product prd1 = new Product(){1,2,34};
Product prd2 = new Product(){2,5,20};
Product prd3 = new Product(){3,6,14};
Product prd4 = new Product(){4,9,8};
Product prd5 = new Product(){5,12,70};
Product prd1b = new Product(){1,60,34};


List<Product> oldLst = new List<Product>(){ prd1,prd2,prd3};
List<Product> newLst = new List<Product>(){ prd1b,prd2,prd4,prd5};

Note that the quantity can change between the old prd1 and the new prd1

My problem is when i use var lstToDel = oldLst.Except(newLst);

lstToDel is filled with oldLst and does not make the difference.

The desired result would be that

lstToDel = new List<Product>(){prd1,prd3};
Biscuit
  • 4,840
  • 4
  • 26
  • 54
  • 4
    Your class doesn't override `Equals` + `GetHashCode` so only references are compared. – Tim Schmelter Mar 17 '16 at 10:56
  • How do i set equals to check the id and the quantity as well ? – Biscuit Mar 17 '16 at 10:59
  • Your code would be a lot clearer if you didn't reuse the name `prd1`, and expressed the whole thing as a [mcve]. At the moment we don't know what the actual result is, or how you're determining that. I don't think this is actually a duplicate of the other question, as it sounds like you actually *want* reference equality for the moment - but without an example of exactly what's happening, it's hard to help. – Jon Skeet Mar 17 '16 at 11:00
  • @Biscuit: `GetHashCode`: http://stackoverflow.com/a/263416/284240 `Equals` should be simple, just compare the properties/fields one after the other. – Tim Schmelter Mar 17 '16 at 11:01
  • @Jon Skeet : Yes but since the products are on the bill and you can change the quantity of the product it reflects exacty how it will be on my code. – Biscuit Mar 17 '16 at 11:01
  • @Biscuit: I'm afraid I don't understand that comment at all. It *really* doesn't help that you've given code which wouldn't compile, and you haven't clearly described the issue. I really want to help, but your question is too unclear at the moment. Are you saying that in reality, you're *not* calling `new Product` for `prd1`, but instead changing the quantity and price of an *existing* object? – Jon Skeet Mar 17 '16 at 11:06
  • 1
    Note that iyou call `prd1.quantity = 60;`, you havereferences to this object in `oldLst` and `newLst`. So both `oldLst.First().quantity` and `newLst.First().quantity` would be equal to 60. Are you sure that is what you want? – Ivan Gritsenko Mar 17 '16 at 11:17
  • 1
    Indeed, this feels like it's a failure of understanding of how reference types works more than a LINQ issue. – Jon Skeet Mar 17 '16 at 11:24

3 Answers3

0

Except also accepts an IEqualityComparer. Create one in order to determine which products are "equal", meaning have the same properties. See more: https://msdn.microsoft.com/en-us/library/bb336390.aspx

Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
0

You should override the Equals on Product and define your criteria to compare a Product. Or define your

class MyComparer: IEqualityComparer<Product>
{
}

if you see the implementation, you should notice that it use Equals, so create your Comparer and you will get the result you need when you call the Except

/// <typeparam name="T">The type of objects to compare.This type parameter is contravariant. That is, you can use either the type you specified or any type that is less derived. For more information about covariance and contravariance, see Covariance and Contravariance in Generics.</typeparam>
  [__DynamicallyInvokable]
  public interface IEqualityComparer<in T>
  {
    /// <summary>
    /// Determines whether the specified objects are equal.
    /// </summary>
    /// 
    /// <returns>
    /// true if the specified objects are equal; otherwise, false.
    /// </returns>
    /// <param name="x">The first object of type <paramref name="T"/> to compare.</param><param name="y">The second object of type <paramref name="T"/> to compare.</param>
    [__DynamicallyInvokable]
    bool Equals(T x, T y);

    /// <summary>
    /// Returns a hash code for the specified object.
    /// </summary>
    /// 
    /// <returns>
    /// A hash code for the specified object.
    /// </returns>
    /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception>
    [__DynamicallyInvokable]
    int GetHashCode(T obj);
  }
}

Here is the Except implementation

 [__DynamicallyInvokable]
    public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      if (first == null)
        throw Error.ArgumentNull("first");
      if (second == null)
        throw Error.ArgumentNull("second");
      else
        return Enumerable.ExceptIterator<TSource>(first, second, comparer);
    }
Zinov
  • 3,817
  • 5
  • 36
  • 70
0

You could also define your own general purpose Extension method, like this:

    public static IEnumerable<T> Except<T>(this IEnumerable<T> collection, IEnumerable<T> comparand, Func<T, object> comparer)
    {
        return collection.Except(comparand, new FuncComparer<T>(comparer));
    }

with a custom Comparer:

public class FuncComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, object> _comparer;

    public FuncComparer(Func<T, object> comparer)
    {
        _comparer = comparer;
    }

    public bool Equals(T x, T y)
    {
        return _comparer(x) == _comparer(y);
    }

    public int GetHashCode(T obj)
    {
        return _comparer(obj).GetHashCode();
    }
}

You then can use it like this:

var lstToDel = oldLst.Except(newLst, x=>x.id_prod); // your comparer logic is on id_prod property

This will work however, only on simple property comparisons. For more sophisticated equality comparers, is best to make your own custom IEqualityComparer.

Tamas Ionut
  • 4,240
  • 5
  • 36
  • 59