Сравнение двух списков и игнорирование определенного свойства - PullRequest
7 голосов
/ 23 марта 2012

У меня есть два списка сотрудников, из которых я хочу получать только уникальные записи, но в этом есть и поворотВ каждом списке есть класс Employee:

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; }
}

Единственными свойствами, с которыми я хочу сравнить совпадения, являются FirstName и LastName.Я хочу полностью игнорировать ID в сравнении.В списке allFulltimeEmployees 3 сотрудника, а в списке allParttimeEmployees 3 сотрудника.Имя и фамилия совпадают по двум пунктам в списках - Салли Джонс и Фред Джексон.В списке есть один элемент, который не совпадает, поскольку FirstName совпадает, но LastName отличается:

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);

Поэтому я хочу игнорировать свойство ID в классе при сравнении двух списков.Я хочу пометить Джо Уильямса как несоответствующее, поскольку фамилии Смит и Уильямс в двух списках не совпадают.

// finalResult should only have Joe Williams in it 

var finalResult = allFulltimeEmployees.Except(allParttimeEmployees);

Я пытался использовать IEqualityComparer, но с тех пор это не работаетон использует один класс Employee в параметрах, а не список IEnumerable:

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();
        }
    }

Как я могу успешно выполнить то, что я хочу, и выполнить эту операцию?Спасибо за любую помощь!

Ответы [ 5 ]

12 голосов
/ 23 марта 2012

Ваша идея использования IEqualityComparer в порядке, это неправильное выполнение.Примечательно, что ваш GetHashCode метод.

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

IEqualityComparer определяет и Equals, и GetHashCode , потому что оба они важны. Не игнорируйте GetHashCode при реализации этого интерфейса!Это играет ключевую роль в сравнениях на равенство.Нет, это не показатель того, что два элемента равны, но это показатель того, что два элемента не являются .Два равных элемента должны возвращать один и тот же хэш-код.Если они этого не делают, их нельзя считать равными.Если они это сделают, то они могут быть равными, а функции равенства только тогда продолжит изучение Equals.

С вашей реализацией, делегирующей GetHashCodeметод фактического объекта employee, вы полагаетесь на реализацию, которую использует класс Employee. Только если эта реализация переопределена, это будет полезно для вас, а только если использует ваши ключевые поля. А если это , то весьма вероятно, что вам не нужно было сначала определять собственный внешний компаратор!

Создайте метод GetHashCode, который учитывает ключевые поляи вы будете установлены.

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(); 
}

Как только у вас будет этот метод, используйте в своем вызове компаратор для Except.

var comparer = new EmployeeEqualityComparer();
var results = allFulltimeEmployees.Except(allParttimeEmployees, comparer);
3 голосов
/ 23 марта 2012

Вы можете переопределить Equals и GetHashCode в вашем Employees классе.

Например,

    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();
        }
    }

Я тестировал со следующим набором данных:

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.
0 голосов
/ 23 марта 2012

Это не самое элегантное решение, но вы могли бы сделать такую ​​функцию, как это

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

и затем заполните все в allFullTimeEmployees в Dictionary<string, Employee>, где ключ словаря является результатом вызова GetKey для каждого объекта сотрудника. Затем вы можете зацикливаться на allParttimeEmployees и вызывать GetKey для каждого из них, исследовать словарь (например, используя TryGetValue или ContainsKey) и предпринимать любые необходимые действия с дубликатом, такие как удаление дубликата словарь.

0 голосов
/ 23 марта 2012

Попробуйте реализовать интерфейс IEquatable (T) для вашего класса Employee. Вам просто нужно предоставить реализацию для Equals() метода, который вы можете определить по своему усмотрению (то есть игнорировать идентификаторы сотрудников).

Интерфейс IEquatable используется общими объектами коллекции, такими как как словарь, список и LinkedList при тестировании на равенство в таких методах, как Contains, IndexOf, LastIndexOf и Удалить. Это должно быть реализовано для любого объекта, который может быть сохранен в общей коллекции.

Пример реализации метода Equals():

public bool Equals(Employee other)
{
   return (other != null) && (FirstName == other.FirstName) && (LastName == other.LastName);
}
0 голосов
/ 23 марта 2012

ваш IEqualityComparer должен работать:

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