2014-09-15 2 views
4

У меня есть два отдельных списка пользовательских объектов. В этих двух отдельных списках могут быть некоторые объекты, которые идентичны между двумя списками, за исключением одного поля («id»). Я хотел бы знать, как можно запросить эти два списка, чтобы найти это перекрытие. Я приложил код, который поможет уточнить. Мы ценим любые предложения.Linq: найти похожие объекты из двух разных списков

namespace ConsoleApplication1 
{ 
class userObj 
{ 
    public int id; 
    public DateTime BirthDate; 
    public string FirstName; 
    public string LastName; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<userObj> list1 = new List<userObj>(); 
     list1.Add(new userObj() 
     { 
      BirthDate=DateTime.Parse("1/1/2000"), 
      FirstName="John", 
      LastName="Smith", 
      id=0 
     }); 
     list1.Add(new userObj() 
     { 
      BirthDate = DateTime.Parse("2/2/2000"), 
      FirstName = "Jane", 
      LastName = "Doe", 
      id = 1 
     }); 
     list1.Add(new userObj() 
     { 
      BirthDate = DateTime.Parse("3/3/2000"), 
      FirstName = "Sam", 
      LastName = "Smith", 
      id = 2 
     }); 



     List<userObj> list2 = new List<userObj>(); 
     list2.Add(new userObj() 
     { 
      BirthDate = DateTime.Parse("1/1/2000"), 
      FirstName = "John", 
      LastName = "Smith", 
      id = 3 
     }); 
     list2.Add(new userObj() 
     { 
      BirthDate = DateTime.Parse("2/2/2000"), 
      FirstName = "Jane", 
      LastName = "Doe", 
      id = 4 
     }); 


     List<int> similarObjectsFromTwoLists = null; 
     //Would like this equal to the overlap. It could be the IDs on either side that have a "buddy" on the other side: (3,4) or (0,1) in the above case. 

    } 
} 
} 

ответ

5

Я не знаю, почему вы хотите List<int>, я предполагаю, что это то, что вы хотите:

var intersectingUser = from l1 in list1 
         join l2 in list2 
         on new  { l1.FirstName, l1.LastName, l1.BirthDate } 
         equals new { l2.FirstName, l2.LastName, l2.BirthDate } 
         select new { ID1 = l1.id, ID2 = l2.id }; 
foreach (var bothIDs in intersectingUser) 
{ 
    Console.WriteLine("ID in List1: {0} ID in List2: {1}", 
        bothIDs.ID1, bothIDs.ID2); 
} 

Выход:

ID in List1: 0 ID in List2: 3 
ID in List1: 1 ID in List2: 4 
+0

Имхо он ничего не хочет с идентификатором. Он просто хочет идентичные наборы без ID в сравнении. Другими словами: объединение со всеми свойствами, кроме ID. – Marco

+0

Спасибо! Очень полезно! – BenjiFB

1

Вы могли бы просто присоединиться списки на тех, 3 свойства:

var result = from l1 in list1 
       join l2 in list2 
       on new {l1.BirthDate, l1.FirstName, l1.LastName} 
        equals new {l2.BirthDate, l2.FirstName, l2.LastName} 
       select new 
       { 
       fname = l1.FirstName, 
       name = l1.LastName, 
       bday = l1.BirthDate 
       }; 

Вместо простого соединения только одного свойства (столбца) создаются два анонимных объекта new { prop1, prop2, ..., propN}, на которых выполняется соединение. В вашем случае мы принимаем все свойства, кроме Id, который вы хотите быть проигнорированы и вуаля:

Выход:

enter image description here

И Тим опередил меня на минуту

+0

Спасибо вам! – BenjiFB

1

Вы можете реализовать свой собственный IEqualityComparer<T> для своего класса userObj и использовать его для сравнения между двумя списками. Это будет самый эффективный подход.

public class NameAndBirthdayComparer : IEqualityComparer<userObj> 
{ 
    public bool Equals(userObj x, userObj y) 
    { 
     return x.FirstName == y.FirstName && x.LastName == y.LastName && x.BirthDate == y.BirthDate; 
    } 

    public int GetHashCode(userObj obj) 
    { 
     unchecked 
     { 
      var hash = (int)2166136261; 

      hash = hash * 16777619^obj.FirstName.GetHashCode(); 
      hash = hash * 16777619^obj.LastName.GetHashCode(); 
      hash = hash * 16777619^obj.BirthDate.GetHashCode(); 

      return hash; 
     } 
    } 
} 

Вы можете использовать этот comparer так:

list1.Intersect(list2, new NameAndBirthdayComparer()).Select(obj => obj.id).ToList(); 
+1

Интересная идея! Спасибо за предложение ... – BenjiFB

+0

Подход linq будет работать отлично, если вы хотите придерживаться этого, и ваши списки имеют тривиальный размер, но этот подход имеет то преимущество, что логика сравнения в многоразовом месте, если вам это нужно снова, и используя сравнения Hash, которые намного быстрее по сравнению с нетривиальными размерами списка. –

1
 var similarObjectsFromTwoLists = list1.Where(x => 
      list2.Exists(y => y.BirthDate == x.BirthDate && y.FirstName == x.FirstName && y.LastName == x.LastName) 
     ).ToList(); 

Это короче, но для большой список является более эффективным "Intersect" или "Регистрация":

var similarObjectsFromTwoLists = 
    list1.Join(list2, x => x.GetHashCode(), y => y.GetHashCode(), (x, y) => x).ToList(); 

(допускается использование GetHashCode() для userObj)

1
var query = list1.Join (list2, 
       obj => new {FirstName=obj.FirstName,LastName=obj.LastName, BirthDate=obj.BirthDate}, 
       innObj => new {FirstName=innObj.FirstName, LastName=innObj.LastName, BirthDate=innObj.BirthDate}, 
       (obj, userObj) => (new {List1Id = obj.id, List2Id = userObj.id})); 

foreach (var item in query) 
    { 
    Console.WriteLine(item.List1Id + " " + item.List2Id); 
    } 
Смежные вопросы