Каков наилучший способ пересечь эти два списка?

У меня есть 2 списка на С#:

public class AvailableSlot
{
     public DateTime DateTime;
     public string Name
}

 List<AvailableSlot> list1 = GetList();
 List<AvailableSlot> list2 = GetAnotherList();

Я хочу вызвать пересечение в этих списках, чтобы узнать, где в обоих списках есть элементы на одну и ту же дату. Я знаю, что могу использовать .Intersect для получения этой информации, но у меня немного более сложное требование. Я хочу вернуть пересекающийся список, но я хочу, чтобы этот список содержал список объектов со всеми именами в них. так что-то вроде этого:

  List<AvailableSlot2> intersectedList  . ..

где AvailableSlot2 это ниже:

public class AvailableSlot2
{
     public DateTime DateTime;
     public string[] Names;
}

Есть ли возможность сделать это преобразование после попытки пересечения двух списков?


person leora    schedule 16.02.2013    source источник
comment
уникальны ли DateTime внутри списка?   -  person Cristian Lupascu    schedule 16.02.2013


Ответы (3)


Я бы просто объединил два списка, сгруппировал по дате и времени, а затем вытащил имена из группы:

var list1 = new List<AvailableSlot>
{
    new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Alpha" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bravo" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Charlie" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Delta" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Echo" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Foxtrot" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 4), Name = "Golf" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hotel" }
};

var list2 = new List<AvailableSlot>
{
    new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Apple" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bannana" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Dog" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Egg" },
    new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hi" }
};

var list3 = list1.Where (l => list2.Where (li => l.DateTime == li.DateTime).Any ())
   .Union(list2.Where (l => list1.Where (li => l.DateTime == li.DateTime).Any ()));

var groupedItems = from slot in list3
    group slot by slot.DateTime into grp
    select new AvailableSlot2 {
        DateTime = grp.Key,
        Names = grp.Select (g => g.Name).ToArray()
    };

foreach(var g in groupedItems)
{
    Console.WriteLine(g.DateTime);
    foreach(var name in g.Names)
        Console.WriteLine(name);
    Console.WriteLine("---------------------");
}

Выход:

2/1/2013 12:00:00 AM
Alpha
Delta
Apple
Dog
---------------------
2/2/2013 12:00:00 AM
Bravo
Echo
Bannana
Egg
---------------------
2/5/2013 12:00:00 AM
Hotel
Hi
---------------------
person Brad Rem    schedule 16.02.2013
comment
по какой-то причине я все еще получаю результаты в моем окончательном списке, который существовал только в одном из списков (не в обоих). .все еще отладка - person leora; 16.02.2013
comment
@leora, хорошо, добавил код для истинного пересечения, так что DateTime должен быть в обоих списках. См. закомментированный код выше. - person Brad Rem; 16.02.2013

Вы можете использовать LINQ to Objects Join() для выравнивания элементов с одним и тем же свойством DateTime, а затем собрать все имена в массив.

var joinedItems = from slot1 in list1
                  join slot2 in list2
                  on slot1.DateTime equals slot2.DateTime into g
                  where g.Any()
                  select new AvailableSlot2
                  {
                      DateTime = slot1.DateTime,
                      Names = Enumerable.Range(slot1.Name,1).Union(g.Select(s => s.Name)).ToArray()
                  }
person cordialgerm    schedule 16.02.2013
comment
Enumerable.Range(), кажется, хочет 2 аргумента int, поэтому это не компилируется - person leora; 16.02.2013

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

DateTime dt1 = new DateTime(2013, 2, 1);
DateTime dt2 = new DateTime(2013, 3, 1);
DateTime dt3 = new DateTime(2013, 4, 1);

var list1 = new List<AvailableSlot>
{
    new AvailableSlot{DateTime = dt1, Name = "n1",},
    new AvailableSlot{DateTime = dt2, Name = "n2",},
    new AvailableSlot{DateTime = dt1, Name = "n3",},
};

var list2 = new List<AvailableSlot>
{
    new AvailableSlot{DateTime = dt1, Name = "n1",},
    new AvailableSlot{DateTime = dt2, Name = "n2",},
    new AvailableSlot{DateTime = dt3, Name = "n3",},
};

var intersected = list1.Select (l => l.DateTime).
                        Intersect(list2.Select (l => l.DateTime));

var lookup = list1.Union(list2).ToLookup (
                                slot => slot.DateTime, slot => slot);

lookup.Where (l => intersected.Contains(l.Key)).Select (
    slot => new 
    {
        DateTime=slot.Key, 
        Names=slot.Select (s => s.Name)
    });

Что в данном случае дает результат:

DateTime            Names

01/02/2013 00:00    n1
                    n3
                    n1

01/03/2013 00:00    n2
                    n2

Конечно, вы можете использовать Names=slot.Select(s => s.Name).Distinct(), чтобы получить отдельный список имен.

person Phil    schedule 16.02.2013