1

Firstly, I will say what I want to compare the following: My Custom Object (Item) has a List of strings taxids. I want to look if all the strings in one List occur in another list of strings (will be also another taxids of another Object (Item).

So, this is the class:

public class Item
{
    public long taxid { get; set; }
    public long contentid { get; set; }
    public string taxname { get; set; }
    public IEnumerable<string> taxids { get; set; }
}

Then these are the dummy custom objects:

    List<string> numbers = new List<string> { "5", "1" };
    List<string> numbers2 = new List<string> { "1", "2", "5","3","564" };

    Item pr = new Item();
    pr.contentid = 2517;
    pr.taxid = 2246;
    pr.taxids = numbers.AsEnumerable();
    pr.taxname = "nameItem1";
    List<Item> te = new List<Item>();
    te.Add(pr);
    IQueryable<Item> er = te.AsQueryable();

    Item pr2 = new Item();
    pr2.contentid = 0;
    pr2.taxid = 0;
    pr2.taxids = numbers2.AsEnumerable();
    pr2.taxname = "nameItem2";
    List<Item> te2 = new List<Item>();
    te2.Add(pr2);
    IQueryable<Item> er2 = te2.AsQueryable();

    IQueryable<Item> both = er.Intersect(er2, new ItemComparer());

Here I use a custom comparer ItemComparer. Here is the code for this:

public class ItemComparer : IEqualityComparer<Item>
{
    // items are equal if their names and item numbers are equal.
    public bool Equals(Item x, Item y)
    {
        //Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        //Check whether the items' properties are equal.
        return x.taxids.Intersect(y.taxids).SequenceEqual(x.taxids);
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.
    public int GetHashCode(Item item)
    {
        //Check whether the object is null
        if (Object.ReferenceEquals(item, null)) return 0;

        //Get hash code for the Name field if it is not null.
        int hashItemName = item.taxids == null ? 0 : item.taxids.GetHashCode();

        //Calculate the hash code for the item.
        return item.taxids.GetHashCode();

        //return "a".GetHashCode();
    }
}

The problem is that variable both has nothing in the taxids field, normally I should have a list of "5" "1".

I know that the hashCode must be the same when comparing. but taxids will be never the same. Because we look for strings in another list of strings.

Anybody can further help me with this problem?

(Also a small question: If I return always the same hashcode for everything like "a".GetHashCode() => should this work or not?

Thanks in advance

Ozkan
  • 2,011
  • 6
  • 29
  • 43

2 Answers2

2

I think your problem is that you do not have bidirectional equality. depending on which side your object is, pr and pr 2 are not equal.

I am uncertain whether there is a guarantee which object is x and which is y in your comparer. What if pr2 ends up to be x in your comparer?

with regard to the hash code, you are doing taxids.GetHashCode() - that is just a list and not a name and won't tell you anything about equality.

flq
  • 22,247
  • 8
  • 55
  • 77
0
return x.taxids.Intersect(y.taxids).SequenceEqual(x.taxids);

Your equality comparison does currently not take into account the different order of elements in the collections. As a crude workaround you could order them before comparing, or better add the items to a hashset:

return x.taxids.Intersect(y.taxids)
               .OrderBy(x => x)
               .SequenceEqual(x.taxids.OrderBy(x => x));

Also you have to provide an appropriate implementation of GetHashCode(), the default implementation is not dependent on the actual items in the list, so wouldn't result in the same hashcode for two different Item instances that have the same elements in the collection. A more appropriate implementation for your case can be found in this other SO post.

Community
  • 1
  • 1
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • When doing a instersect, it returns always in the same order. But thanks for the information – Ozkan Dec 19 '11 at 10:43
  • It will return elements in the order they have been collected (and even that you should not rely on) - in one of the collections. This is not necessarily the same as items in the other collection hence the re-ordering is needed. – BrokenGlass Dec 19 '11 at 10:52
  • Thanks for the information. And for the getHashcode method. Maybe you know more than me. Can I use always the same hashcode for everything? For example: `"a".GetHashCode()`. Because I will never have two lists that are exactly the same. In the URL you gave, the lists can be made the same by doing distinct, but I have totally different items in the list. – Ozkan Dec 19 '11 at 11:57