Comparing two lists and ignoring a specific property

I have two lists of employees from which I want to get only unique records, but it has a twist. Each list has an Employee class:

public class Employee { // I want to completely ignore ID in the comparison public int ID{ get; set; } // I want to use FirstName and LastName in comparison public string FirstName{ get; set; } public string LastName{ get; set; } } 

The only properties that I want to compare to match are FirstName and LastName. I want to completely ignore the ID in comparison. There are 3 employees on the allFulltimeEmployees list, and 3 employees on the allParttimeEmployees list. The first name and surname coincide on two points in the lists - Sally Jones and Fred Jackson. There is one element in the list that does not match, because FirstName is the same, but LastName is different:

 emp.id = null; // not populated or used in comparison emp.FirstName = "Joe"; // same emp.LastName = "Smith"; // different allFulltimeEmployees.Add(emp); emp.id = 3; // not used in comparison emp.FirstName = "Joe"; // a match emp.LastName = "Williams"; // not a match - different last name allParttimeEmployees.Add(emp); 

Therefore, I want to ignore the ID property in the class when comparing two lists. I want to mark Joe Williams as a mismatch, since the last names of Smith and Williams in the two lists do not match.

 // finalResult should only have Joe Williams in it var finalResult = allFulltimeEmployees.Except(allParttimeEmployees); 

I tried to use IEqualityComparer, but it does not work, since it uses only one Employee class in the parameters, and not in the IEnumerable list:

 public class EmployeeEqualityComparer : IEqualityComparer<Employee> { public bool Equals(Employee x, Employee y) { if (x.FirstName == y.FirstName && x.LastName == y.LastName) { return true; } else { return false; } } public int GetHashCode(Employee obj) { return obj.GetHashCode(); } } 

How can I successfully complete what I want and complete this operation? Thanks for any help!

+6
source share
5 answers

Your idea is to use IEqualityComparer in order, this is the wrong execution. Notably, your GetHashCode method.

 public int GetHashCode(Employee obj) { return obj.GetHashCode(); } 

IEqualityComparer defines both Equals and GetHashCode because they are important. Do not ignore GetHashCode when implementing this interface! It plays a key role in comparing comparisons. No, this is not an indicator that the two elements are equal, but it is an indicator that is not two elements. Two equal elements must return the same hash code. If they do not, they cannot be considered equal. If they do, then they can be equal, and the equality functions will only then begin to explore the Equals .

When your implementation delegates the GetHashCode method to the actual employee object, you rely on the implementation that the Employee class uses. Only if this implementation is overridden will it be useful to you, and only if it uses your key fields. And if so, then it is very likely that you did not need to define your own external comparator in the first place!

Create a GetHashCode method that affects your key fields and you will be set.

 public int GetHashCode(Employee obj) { // null handling omitted for brevity, but you will want to // handle null values appropriately return obj.FirstName.GetHashCode() * 117 + obj.LastName.GetHashCode(); } 

After you use this method, use the mapping in your Except call.

 var comparer = new EmployeeEqualityComparer(); var results = allFulltimeEmployees.Except(allParttimeEmployees, comparer); 
+11
source

You can override Equals and GetHashCode in your Employees class.

For instance,

  public class Employee { // I want to completely ignore ID in the comparison public int ID { get; set; } // I want to use FirstName and LastName in comparison public string FirstName { get; set; } public string LastName { get; set; } public override bool Equals(object obj) { var other = obj as Employee; return this.FirstName == other.FirstName && this.LastName == other.LastName; } public override int GetHashCode() { return this.FirstName.GetHashCode() ^ this.LastName.GetHashCode(); } } 

I tested with the following dataset:

 var empList1 = new List<Employee> { new Employee{ID = 1, FirstName = "D", LastName = "M"}, new Employee{ID = 2, FirstName = "Foo", LastName = "Bar"} }; var empList2 = new List<Employee> { new Employee { ID = 2, FirstName = "D", LastName = "M" }, new Employee { ID = 1, FirstName = "Foo", LastName = "Baz" } }; var result = empList1.Except(empList2); // Contained "Foo Bar", ID #2. 
+2
source

your IEqualityComparer should work:

 var finalResult = allFulltimeEmployees.Except(allParttimeEmployees, new EmployeeEqualityComparer()); 
0
source

Try implementing IEquatable (T) for your Employee class. You just need to provide an implementation for the Equals() method, which you can determine as you want (i.e., Ignore employee IDs).

The IEquatable interface is used by universal collection objects such as a dictionary, list, and LinkedList when testing for equality in methods such as Contains, IndexOf, LastIndexOf, and Delete. It must be implemented for any object that can be stored in a common collection.

An example implementation of the Equals() method:

 public bool Equals(Employee other) { return (other != null) && (FirstName == other.FirstName) && (LastName == other.LastName); } 
0
source

This is not the most elegant solution, but you can make such a function

 public string GetKey(Employee emp) { return string.Format("{0}#{1}", emp.FirstName, emp.LastName) } 

and then fill everything in allFullTimeEmployees in the Dictionary<string, Employee> , where the dictionary key is the result of calling GetKey for each employee object. Then you can run the allParttimeEmployees and call GetKey for each of them, probing the dictionary (for example, using TryGetValue or ContainsKey ), and take any actions necessary to duplicate, for example, removing the duplicate from the dictionary.

0
source

Source: https://habr.com/ru/post/911468/


All Articles