Question

I have a person class like so:

Public Class Person
{
public int Id {get; set;}
public int FirstName {get; set;}
public int LastName {get; set;}
}

I create a list of Person objects:

List<Person> AllPersons = GetAllPersons();

In GetAllPersons() I have this code which gives unique and non-repeating person objects based on Id.

        return this.colLoadExternalData.GroupBy(p => p, new MyClass()).Where(g => g.Count() == 1).Select(g => g.Single()).ToList();

Meaning if the source had two persons with Id = 123, then 123 does not appear in the list.

Here's the class implementing IEqualityComparer

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

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

MyClass set's the identity based on Id.

I have an external config file in which there is a provison to set the identity property of the person class, like FirstName or LastName etc., which will identify a unique person based on that property value.

So if the config file says 'FirstName' as the key then MyClass would look like this:

public class MyClass : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.FirstName == y.FirstName;
    }

    public int GetHashCode(Person obj)
    {
        return obj.FirstName.GetHashCode();
    }
} 

I had actually used a dictionary before adding person objects to it but that still adds one person object if the source has repeating entries like two persons with id 123. If this can be done with dictionary object making sure that the final collection does have unique objects and not include repeating records from external source that would be great.

What I could do is maintain a list of duplicates in a separate list. On that list do a simple foreach and remove those person objects from the dictionary like so:

foreach(var dup in Duplicates)
{
  UniquePersonObjects.Remove(dup);
}

Here there is no IEqualityComparer used, just add records from external source to the dictionary and then further filter that dictionary based on the duplicates list.

Is there a better way to do this?

Regards.

Was it helpful?

Solution 2

You could use the configuration in MyClass to handle the different checks:

public class MyClass : IEqualityComparer<Person>
{
    // here you set the configuration
    private PersonCompareConfiguration personCompareConfiguration;

    public bool Equals(Person x, Person y)
    {
        switch(personCompareConfiguration)
        {
            case CompareId:
                return x.Id == y.Id;
                break;
            case CompareFirstname:
                return x.FirstName == y.FirstName;
                break;
        }
        throw new InvalidArgumentException("personCompareConfiguration cannot be handled");
    }

    public int GetHashCode(Person obj)
    {
        switch(personCompareConfiguration)
        {
            case CompareId:
                return obj.Id.GetHashCode();
                break;
            case CompareFirstname:
                return obj.FirstName.GetHashCode();
                break;
        }
        throw new InvalidArgumentException("personCompareConfiguration cannot be handled");
    }
}

public enum PersonCompareConfiguration
{
    CompareId,
    CompareFirstname
}

Another (smarter) way would be to use MEF to export an implementation of IEqualityComparer<Person>. Have a look at this answer which describes how to export and import generic interfaces.

OTHER TIPS

The best implementation I manage to find so far you can find on following link

public class EqualityComparer:IEqualityComparer { private Func equalsFunction; private Func getHashCodeFunction;

public EqualityComparer(Func<T, T, bool> equalsFunction)
{
    this.equalsFunction = equalsFunction;
}

public EqualityComparer(Func<T, T, bool> equalsFunction, 
    Func<T, int> getHashCodeFunction) : this(equalsFunction)
{
    this.getHashCodeFunction = getHashCodeFunction;
}

public bool Equals(T a, T b)
{
    return equalsFunction(a, b);
}

public int GetHashCode(T obj)
{
    if (getHashCodeFunction == null)
        return obj.GetHashCode();
    return getHashCodeFunction(obj);
}

}

Now you can write your own method for filtering, for instance:

EqualityComparer equalityComparer = new EqualityComparer((s1, s2) => s1.Id == s2.Id);

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top