Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare two objects based on criteria

Tags:

string

c#

linq

I need to compare two List<object> but during comparison for properties having "string" value I don't want case sensitive comparison.

I have a class:

class User
{
    public int Id { get;set; }
    public string name { get;set; }
}

I have 2 lists List<User> olduser and List<User> newuser. I need to compare both lists but while comparing I should ignore case sensitivity of "name" field and get values in olduser not part of values in newuser.

List<User> obsoleteUsers = olduser.Except(newuser).ToList();

I need to add a condition that while comparing two lists, please ignore the case for "name" field.

like image 622
user11708636 Avatar asked Dec 11 '22 02:12

user11708636


2 Answers

You can use a custom IEqualityComparer<T>:

class UserNameComparer : IEqualityComparer<User>
{
    public UserNameComparer(StringComparer comparer)
    {
        if (comparer == null) throw new ArgumentNullException(nameof(comparer));
        this.Comparer = comparer;
    }

    public StringComparer Comparer { get; }

    public bool Equals(User x, User y)
    {
        if (x == null || y == null) return true;
        return Comparer.Equals(x.name, y.name);
    }

    public int GetHashCode(User obj)
    {
        return Comparer.GetHashCode(obj?.name);
    }
}

You use it in Except(or other LINQ methods):

List<User> obsoleteUsers = olduser
    .Except(newuser, new UserNameComparer(StringComparer.InvariantCultureIgnoreCase))
    .ToList();

On this way you can implement multiple comparers for different requirements without changing the original class and the way it identifies duplicates(for example by the ID-property).

Note that Except(and other set based methods like Distinct) use GetHashCode to fast-check if an object equals another. That's why your class should override Equals and GetHashCode(always together) to support being used in a set based collection(like HashSet<T> or Dictionary<TKey, TValue>). Otherwise you will use the version from System.Object that just compares references and not properties.

like image 76
Tim Schmelter Avatar answered Dec 27 '22 16:12

Tim Schmelter


If you want to compare for equality with your own rules, let's implement Equals and GetHashCode methods:

  class User : IEquatable<User> {
    // Dangerous practice: Id (and name) usually should be readonly:
    // we can put instance into, say, dictionary and then change Id loosing the instance 
    public int Id { get; set; }
    public string name { get; set; }

    public bool Equals(User other) {
      if (null == other)
        return false;

      return  
        Id == other.Id && 
        string.Equals(name, other.name, StringComparison.OrdinalIgnoreCase);
    }

    public override bool Equals(object obj) => Equals(obj as User);

    public override int GetHashCode() => Id;
  }

Then you can put Except as usual

like image 39
Dmitry Bychenko Avatar answered Dec 27 '22 16:12

Dmitry Bychenko