0

So, here's the problem:

I have a list of products:

    public class ProductWithFeatures
    {
        public string Name { get; set; }
        public ICollection<Feature> Features { get; set; }
    }

    public class Feature 
    {
        public int Id { get; set; }
    
        public Feature(int Id)
        {
            this.Id = Id;
        }
    }

I need to filter the list, using Linq functions, so that the only remaining items are the products that have at least one feature from a list of given features.

This is what i wrote:

public class ProductListFilteringClass
{
    public List<ProductWithFeatures> products;

    public ProductListFilteringClass(List<ProductWithFeatures> list)
    {
        this.products = list;
    }

    public List<ProductWithFeatures> ProductsWithAtLeastOneFeatureFromTheList(ICollection<Feature> features)
    {
        Func<ProductWithFeatures, bool> doesItHaveTheFeatures = x =>
        {
            FeatureComparer comparer = new FeatureComparer();
            bool b = x.Features.Any(y => features.Contains(y, comparer));
            return b;
        };

        return products.Where(doesItHaveTheFeatures).ToList();
    }
}

public class FeatureComparer : IEqualityComparer<Feature>
{
    public bool Equals(Feature x, Feature y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(Feature obj)
    {
        return obj.Id;
    }
}

Problem is, when i try to run a test on this, it results the correct answer but it doesn't match. Here's the test:

[Fact]
    public void CheckIfReturnProductsWithAtLeastOneFeatureFromTheListWorksCorrectly()
    {
        LinqHomework.Feature[] ids = new LinqHomework.Feature[6];
        ids[0] = new LinqHomework.Feature(1);
        ids[1] = new LinqHomework.Feature(3);
        ids[2] = new LinqHomework.Feature(5);
        ids[3] = new LinqHomework.Feature(7);
        ids[4] = new LinqHomework.Feature(9);
        ids[5] = new LinqHomework.Feature(11);

        List<LinqHomework.ProductWithFeatures> list = new List<LinqHomework.ProductWithFeatures>();
        LinqHomework.Feature[] featuresA = new LinqHomework.Feature[3];
        featuresA[0] = new LinqHomework.Feature(0);
        featuresA[1] = new LinqHomework.Feature(5);
        featuresA[2] = new LinqHomework.Feature(2);
        LinqHomework.ProductWithFeatures productA = new LinqHomework.ProductWithFeatures();
        productA.Name = "a";
        productA.Features = featuresA;
        list.Add(productA);

        LinqHomework.Feature[] featuresB = new LinqHomework.Feature[3];
        featuresB[0] = new LinqHomework.Feature(1);
        featuresB[1] = new LinqHomework.Feature(3);
        featuresB[2] = new LinqHomework.Feature(7);
        LinqHomework.ProductWithFeatures productB = new LinqHomework.ProductWithFeatures();
        productB.Name = "b";
        productB.Features = featuresB;
        list.Add(productB);

        LinqHomework.Feature[] featuresC = new LinqHomework.Feature[3];
        featuresC[0] = new LinqHomework.Feature(10);
        featuresC[1] = new LinqHomework.Feature(4);
        featuresC[2] = new LinqHomework.Feature(8);
        LinqHomework.ProductWithFeatures productC = new LinqHomework.ProductWithFeatures();
        productC.Name = "c";
        productC.Features = featuresC;
        list.Add(productC); 

        LinqHomework.ProductListFilteringClass productList = new LinqHomework.ProductListFilteringClass(list);

        List<LinqHomework.ProductWithFeatures> final = new List<LinqHomework.ProductWithFeatures>();

        LinqHomework.Feature[] features1 = new LinqHomework.Feature[3];
        features1[0] = new LinqHomework.Feature(0);
        features1[1] = new LinqHomework.Feature(5);
        features1[2] = new LinqHomework.Feature(2);
        LinqHomework.ProductWithFeatures product1 = new LinqHomework.ProductWithFeatures();
        product1.Name = "a";
        product1.Features = features1;
        final.Add(product1);

        LinqHomework.Feature[] features2 = new LinqHomework.Feature[3];
        features2[0] = new LinqHomework.Feature(1);
        features2[1] = new LinqHomework.Feature(3);
        features2[2] = new LinqHomework.Feature(7);
        LinqHomework.ProductWithFeatures product2 = new LinqHomework.ProductWithFeatures();
        product2.Name = "b";
        product2.Features = features2;
        final.Add(product2);

        var x = new ProductComparer();
        Assert.Equal(final, productList.ProductsWithAtLeastOneFeatureFromTheList(ids), x);
    }

    public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
    {
        public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
        {
            return x.Name == y.Name && x.Features == y.Features;
        }

        public int GetHashCode(ProductWithFeatures obj)
        {
            string toHash = obj.Name;
            foreach (var feature in obj.Features)
                toHash += feature.GetHashCode();

            return toHash.GetHashCode();
        }
    }

And here's the result:

Result Message: 
Assert.Equal() Failure
Expected: List<ProductWithFeatures> [ProductWithFeatures { Features = [...], Name = "a" }, ProductWithFeatures { Features = [...], Name = "b" }]
Actual:   List<ProductWithFeatures> [ProductWithFeatures { Features = [...], Name = "a" }, ProductWithFeatures { Features = [...], Name = "b" }]

It's identical, but it doesn't match. How can i fix this?

  • 1
    Did you debug it? Does the list have the correct number of items? Hint: I don't think your product comparer does what you think it does. – nvoigt Jul 07 '20 at 09:37
  • @nvoigt i did debug it, and it should work properly. Do you have any idea how to write the product comparer better? –  Jul 07 '20 at 09:40
  • Uh, you posted here because it doesn't work properly. The word "should" has no place in debugging, the point of debugging is to find out what **is**, not what **should be**. So the first step is to debug it and find out why. set a breakpoint to the line with the assert and check both your result and the expected result and see if they match. If they don't, your function is wrong and you need to debug that. If they do, your checking is wrong and you need to debug that. – nvoigt Jul 07 '20 at 09:43
  • @nvoigt the items are identical. i debugged it, and they are ok. –  Jul 07 '20 at 09:50
  • Well, if they are identical, then your way of comparing them must be wrong, right? You need to debug that next. – nvoigt Jul 07 '20 at 10:01

2 Answers2

0

I would be looking at this part of your equality check:

        public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
        {
            return x.Name == y.Name && x.Features == y.Features;
        }

The name comparison will work fine, but you are using the == operator to compare two collections, which will give you a reference equality check (is this the same instance of the collection) when it looks like you are interested in the values. You could try x.Features.SequenceEquals(y.Features) if you are confident they will be in the same order. More on list equality here.

glenatron
  • 11,018
  • 13
  • 64
  • 112
0

Your ProductComparer method is comparing Features without using your special FeatureComparer method.

Try

public class ProductComparer : IEqualityComparer<ProductWithFeatures>
{
    FeatureComparer featureComparer = new FeatureComparer();
        
    public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
    {
        return x.Name == y.Name && 
            System.Linq.Enumerable.SequenceEqual( x.Features , y.Features, featureComparer);
    }

    ...
sgmoore
  • 15,694
  • 5
  • 43
  • 67