Равенство для 2 списков разных типов - PullRequest
4 голосов
/ 04 января 2011

Предположим, у нас есть 2 коллекции, по крайней мере, IEnumerable для включения в Linq (также предположим, что .net 4.0):

List<T1> list1;  
List<T2> list2;

Я могу определить равенство между объектами типа T1 и T2.

  1. Каков наилучший способ (т. Е. Интерфейс .net и предпочтительный Linq), чтобы проверить, равны ли два списка (порядок элементов не имеет значения).

  2. Как я могу оптимизировать эту проблему, если я знаю, что объекты T1 и T2 имеют идентификатор

Ex для T1 и T2:

class Device 
{
    string Id;
    string Name;
}

class DeviceInfo
{ 
    string Identifier;
    string FriendlyName;
    DateTime CreateDate;
}

Позднее редактировать:

Решение должно включать какой-то компаратор равенства, который я пишу, и он достаточно общий. Могут быть случаи, когда 2 объекта имеют одинаковые идентификаторы, но разные имена, и сравнение должно завершиться неудачно. Например:

static bool AreEqual(Device device, DeviceInfo deviceInfo)
{
     return device.Id == deviceInfo.Identifier &&
            device.Name == deviceInfo.FriendlyName;
}

Ответы [ 4 ]

3 голосов
/ 04 января 2011

Предполагая .NET 4.0:

Foo[] foos = new Foo[];
Bar[] bars = new Bar[];

var areDifferent = foos.Zip(bars, (foo, bar) => foo.Id == bar.Id).Any(b => !b);

Лучшее решение также проверяет, что foos и bars имеют одинаковую длину, и что ни один из элементов также не является null.И, конечно, в этом примере предполагается, что коллекции уже отсортированы по Id.

Обновление:

Итак, вот «лучшее решение» во всех его LINQyдеталь:

var areDifferent = foos.Count() != bars.Count() ||
                   foos.OrderBy(foo => foo.Id)
                   .Zip(
                       bars.OrderBy(bar => bar.Id),
                       (foo, bar) => foo != null && bar != null && foo.Id == bar.Id)
                   .Any(b => !b);
2 голосов
/ 04 января 2011

Вы можете сделать что-то вроде этого:

List<Device> devices = ...
List<DeviceInfo> deviceInfos = ...

var deviceIds = devices.Select(d => d.Id)
                       .OrderBy(id => id);

var deviceInfoIds = deviceInfos.Select(d => d.Identifier)
                               .OrderBy(id => id);

bool areEqual = deviceIds.SequenceEqual(deviceInfoIds);

Если дублирующиеся идентификаторы невозможны, семантика набора пригодится:

bool areEqual = !devices.Select(d => d.Id)
                        .Except(deviceInfos.Select(d => d.Identifier))
                        .Any();

Я бы порекомендовал, если это возможно, объявить интерфейс IHasId (или аналогичный) и получить оба типа для его реализации.

EDIT :

В ответ на ваши изменения вы могли бы написать реализацию IEqualityComparer, которая делала то, что вы хотели. Это выглядело бы действительно ужасно; вам нужно будет сделать умозрительное приведение от каждого аргумента к DeviceInfo / Device, чтобы попытаться извлечь идентификатор. Я не очень рекомендую это; плохая идея для сравнения равенства сравнивать объекты совершенно разных типов. Было бы на много проще, если бы каждый тип реализовывал общий интерфейс, предоставляющий идентификатор.

0 голосов
/ 15 мая 2013

Попробуйте получить разницу между двумя разными списками: если у них есть какое-либо общее свойство.

 var differentItems = List<Type1>.Select(d => d.Name)
                        .Except(List<Type2>.Select(d => d.Name));
0 голосов
/ 04 января 2011

Сравнение 2 списков строк не очень сложно.Оба решения, основанные на упорядочении списков, имеют сложность N log (N) без учета размера строки.Лучшее решение (псевдокод), сложность N:

create a dictionary<string, int>

foreach element in list1
if element is in dict
 dict[element]++;
else
 dict[element] = 1;

foreach element in list2
if element is in dict
 dict[element]--;
else 
 return NOT_EQUAL;

if dict has only 0 values lists are equal
...