2012-03-23 5 views
6

У меня есть два списка сотрудников, из которых я хочу получить только уникальные записи, но у этого есть поворот. Каждый список имеет класс 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(); 
     } 
    } 

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

ответ

11

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

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

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

С вашей реализацией, делегирующей метод GetHashCode объекта фактического сотрудника, вы полагаетесь на реализацию, которую использует класс 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); 
+1

Спасибо Anthony - это была именно проблема. Как только я исправил свой метод GetHashCode, он работал нормально. В чем смысл «117»? Просто добавить дополнительную уникальность в расчетное значение? Кроме того, как я могу обращаться с нулями в методе GetHashCode? Еще раз спасибо за ответ! – Frekster

+1

Математика является кивком для реализаций, таких как [рекомендуемые здесь] (http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode) , Подробнее о правилах и рекомендациях для GetHashCode, [это отличное чтение] (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx). –

+1

Что касается нулей, вы просто не хотите получать доступ к свойствам объектов null Employee и не хотите вызывать методы для свойств null. Поэтому вы хотите иметь соответствующие нулевые проверки, если у вас есть какая-либо озабоченность по поводу наличия нулей. –

2

Вы можете переопределить 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

Это действительный * только если * идентификатор * никогда * не является фактором сравнения равенств. (Однако, вообще говоря, вы склонны думать, что идентификатор важен. Если он не может * идентифицировать * объект, что хорошего в этом?) В этом случае, по-видимому, это особый случайный сценарий, который исключается для равенства сравнение, что делает внешний компаратор правильным. –

0

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

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

Его нынешняя реализация его компаратора привела бы к правильным результатам случайно или с избытком. А именно, он должен был бы иметь подходящую стандартную реализацию GetHashCode, уже определенную внутри класса, что делает его внешний сопоставитель по сравнению излишним. –

0

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

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

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

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

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

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

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

+0

Это сработает, но это не очень элегантно. – Bernard

 Смежные вопросы

  • Нет связанных вопросов^_^