snowYetis
snowYetis

Reputation: 1627

c# Linq Except Not Returning List of Different Values

I am trying to find the differences in two lists. List, "y" should have 1 unique value when compared to list "x". However, Except, does not return the difference. The, "differences" list's count always equals 0.

List<EtaNotificationUser> etaNotifications = GetAllNotificationsByCompanyIDAndUserID(PrevSelectedCompany.cmp_ID);
IEnumerable<string> x = etaNotifications.OfType<string>();
IEnumerable<string> y = EmailList.OfType<string>();

IEnumerable<string> differences = x.Except(y,  new StringLengthEqualityComparer()).ToList();

   foreach(string diff in differences)
                {
                    addDiffs.Add(diff);
                }

After reading a few posts and articles on the post, I created a custom comparer. The comparer looks at string length (kept it simple for testing) and obtains the Hashcode, since these are two objects of a different type (even though I convert their types to string), I thought it may have been the issue.

class StringLengthEqualityComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return x.Length == y.Length;
    }

    public int GetHashCode(string obj)
    {
        return obj.Length;
    }
}

This is my first time using Except. Sounds like a great, optimized way of comparing two lists, but I can't get it to work.

Update

X - Should hold Email Addresses from the database. GetAllNotificationsByCompanyIDAndUserID - brings back email values from the DB. Y - Should hold all Email Addresses in the UI Grid.

What I am trying to do is detect if a new e-mail has been added to the grid. So at this point X will have the saved values from past entries. Y will have any new e-mail addresses add by the user and have not been saved yet.

I have verified this is all working correctly.

Upvotes: 0

Views: 2622

Answers (2)

dbc
dbc

Reputation: 116544

Assuming you have verified that your two enumerables x and y actually contain the strings you expect them to, I believe your problem is with your string comparer. According to the docs, Enumerable.Except "Produces the set difference of two sequences. The set difference is the members of the first sequence that don't appear in the second sequence." But your equality comparer equates all strings with the same length. Thus, if a string in the first sequence happens to have the same length as a string in the second, it will not be found as different using your comparer.

Update: yup, I just tested it:

public class StringLengthEqualityComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return x.Length == y.Length;
    }

    public int GetHashCode(string obj)
    {
        return obj.Length;
    }
}


        string [] array1 = new string [] { "foo", "bar", "yup" };
        string[] array2 = new string[] { "dll" };
        int diffCount;

        diffCount = 0;
        foreach (var diff in array1.Except(array2, new StringLengthEqualityComparer()))
        {
            diffCount++;
        }

        Debug.Assert(diffCount == 0); // No assert.

        diffCount = 0;
        foreach (var diff in array1.Except(array2))
        {
            diffCount++;
        }

        Debug.Assert(diffCount == 0); // Assert b/c diffCount == 3.

There is no assert with the custom comparer but there is with the standard.

Upvotes: 1

D Stanley
D Stanley

Reputation: 152511

The problem is here:

IEnumerable<string> x = etaNotifications.OfType<string>();

but etaNotifications is a List<EtaNotificationUser>, none of which can be a string since string is sealed. OfType returns all instances that are of the given type - it does not "convert" each member to that type.

So x will always be empty.

Maybe you want:

IEnumerable<string> x = etaNotifications.Select(e => e.ToString());

if EtaNotificationUser has overridden ToString to give you the value you want to compare. If the value you want to compare is in a property you can use:

IEnumerable<string> x = etaNotifications.Select(e => e.EmailAddress);

or some other property.

You'll likely have to do something similar for y (unless EmailList is already a List<string> which I doubt).

Upvotes: 4

Related Questions