1

I have a table that has combo pairs identifiers, and I use that to go through CSV files looking for matches. I'm trapping the unidentified pairs in a List, and sending them to an output box for later addition. I would like the output to only have single occurrences of unique pairs. The class is declared as follows:

    public class Unmatched:IComparable<Unmatched>
{
    public string first_code { get; set; }
    public string second_code { get; set; }

    public int CompareTo(Unmatched other)
    {

        if (this.first_code == other.first_code)
        {
            return this.second_code.CompareTo(other.second_code);
        }

        return other.first_code.CompareTo(this.first_code);
    }
 }

One note on the above code: This returns it in reverse alphabetical order, to get it in alphabetical order use this line:

return this.first_code.CompareTo(other.first_code);

Here is the code that adds it. This is directly after the comparison against the datatable elements

unmatched.Add(new Unmatched() 
{ first_code = fields[clients[global_index].first_match_column]
, second_code = fields[clients[global_index].second_match_column] });

I would like to remove all pairs from the list where both first code and second code are equal, i.e.;

PTC,138A
PTC,138A
PTC,138A
MA9,5A
MA9,5A
MA9,5A
MA63,138A
MA63,138A
MA59,87BM
MA59,87BM

Should become:

PTC, 138A
MA9, 5A
MA63, 138A
MA59, 87BM

I have tried adding my own Equate and GetHashCode as outlined here: http://www.morgantechspace.com/2014/01/Use-of-Distinct-with-Custom-Class-objects-in-C-Sharp.html

The SE links I have tried are here:

How would I distinct my list of key/value pairs

Get list of distinct values in List<T> in c#

Get a list of distinct values in List

All of them return a list that still has all the pairs. Here is the current code (Yes, I know there are two distinct lines, neither appears to be working) that outputs the list:

    parser.Close();
    List<Unmatched> noDupes = unmatched.Distinct().ToList();
    noDupes.Sort();
    noDupes.Select(x => x.first_code).Distinct();

    foreach (var pair in noDupes)
    {
        txtUnmatchedList.AppendText(pair.first_code + "," + pair.second_code + Environment.NewLine);
    }

Here is the Equate/Hash code as requested:

public bool Equals(Unmatched notmatched)
{

    //Check whether the compared object is null.  
    if (Object.ReferenceEquals(notmatched, null)) return false;

    //Check whether the compared object references the same data.  
    if (Object.ReferenceEquals(this, notmatched)) return true;

    //Check whether the UserDetails' properties are equal.  
    return first_code.Equals(notmatched.first_code) && second_code.Equals(notmatched.second_code);
}

// If Equals() returns true for a pair of objects   
// then GetHashCode() must return the same value for these objects.  

public override int GetHashCode()
{

    //Get hash code for the UserName field if it is not null.  
    int hashfirst_code = first_code == null ? 0 : first_code.GetHashCode();

    //Get hash code for the City field.  
    int hashsecond_code = second_code.GetHashCode();

    //Calculate the hash code for the GPOPolicy.  
    return hashfirst_code ^ hashsecond_code;
}

I have also looked at a couple of answers that are using queries and Tuples, which I honestly don't understand. Can someone point me to a source or answer that will explain the how (And why) of getting distinct pairs out of a custom list?

(Side question-Can you declare a class as both IComparable and IEquatable?)

10
  • It seems like you have a formatting issue here. I can't see why unmatched.Distinct().ToList() would return duplicates otherwise. Are you sure that the strings are actually equal? Commented May 15, 2014 at 18:06
  • 1
    Please add your definition of Equals and GetHashcode since these are used for finding duplicates, not your IComparable<T> implementation. And yes a class can implement both IComparable<T> and IEquatable<T>. Commented May 15, 2014 at 18:07
  • While the first Distinct() should already do the job, please be aware that noDupes.Select(x => x.first_code).Distinct(); does not have any effect, as you are throwing away the result. Commented May 15, 2014 at 18:09
  • @FarhadAliNoo - I added the code that inserts the pairs into the list. Commented May 15, 2014 at 18:10
  • Also, is it intentional that in the expected result, there is some whitespace after the comma, whereas in the original input, there is none? Commented May 15, 2014 at 18:10

2 Answers 2

4

The problem is you are not implementing IEquatable<Unmatched>.

public class Unmatched : IComparable<Unmatched>, IEquatable<Unmatched>

EqualityComparer<T>.Default uses the Equals(T) method only if you implement IEquatable<T>. You are not doing this, so it will instead use Object.Equals(object) which uses reference equality.

The overload of Distinct you are calling uses EqualityComparer<T>.Default to compare different elements of the sequence for equality. As the documentation states, the returned comparer uses your implementation of GetHashCode to find potentially-equal elements. It then uses the Equals(T) method to check for equality, or Object.Equals(Object) if you have not implemented IEquatable<T>.

You have an Equals(Unmatched) method, but it will not be used since you are not implementing IEquatable<Unmatched>. Instead, the default Object.Equals method is used which uses reference equality.

Note your current Equals method is not overriding Object.Equals since that takes an Object parameter, and you would need to specify the override modifier.

Sign up to request clarification or add additional context in comments.

4 Comments

So, I need to change Unmatched:IComparable<Unmatched> to Unmatched:IComparable<Unmatched>, IEquatable<Unmatched>? Do I need to leave in the Hash and Equate overrides?
@JohnP - Yes you need to keep the Equals and GetHashCode methods. Note your current Equals implementation is not an override of Object.Equals, since that takes an Object parameter.
Ok, thanks. I basically cut/pasted/replaced out of the blog link for the Equate and the Hash overrides.
I added the IEquatable declaration and voila. Thank you for the assistance!
1

For an example on using Distinct see here.

You have to implement the IEqualityComparer<TSource> and not IComparable<TSource>.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.