Bogdan Gavril MSFT
Bogdan Gavril MSFT

Reputation: 21458

Equality for 2 lists of different types

Let's assume we have 2 collections, at least IEnumerable to power on Linq (also assume .net 4.0):

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

I can define equality between objects of type T1 and T2.

  1. What is the best way (i.e. .net interface and Linq preferred) to verify if the 2 lists are equal (order of elements is not relevant).

  2. How can I optimize this problem if I know that the objects T1 and T2 have an ID

Ex of T1 and T2:

class Device 
{
    string Id;
    string Name;
}

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

Later edit:

The solution should involve some sort of equality comparer that I write and is generic enough. There may be cases where 2 objects have the same Id but different name, and comparison should then fail. For example:

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

Upvotes: 4

Views: 604

Answers (4)

Nalan Madheswaran
Nalan Madheswaran

Reputation: 10562

Try this to get the difference between two different list: If they have any common property.

 var differentItems = List<Type1>.Select(d => d.Name)
                        .Except(List<Type2>.Select(d => d.Name));

Upvotes: 0

Ani
Ani

Reputation: 113402

You could do something like this:

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

If duplicate Ids are not possible, set semantics will come in handy:

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

I would recommend if possible that you declare an IHasId (or similar) interface and get both types to implement it.

EDIT:

In response to your edit, you could write an IEqualityComparer implementation that did what you wanted. It would look really ugly; you would have to do a speculative cast from each argument to DeviceInfo / Device to try and extract an identifier out. I wouldn't really recommend this; it's a bad idea for an equality-comparer to compare objects of completely different types. It would be a lot easier if you got each type to implement a common interface that provided an identifier.

Upvotes: 2

Bogdan Gavril MSFT
Bogdan Gavril MSFT

Reputation: 21458

Comparing 2 lists of strings is not very complicated. Both solutions based on ordering the lists have N log (N) complexity not taking into account the size of the string. The better solution is (pseudocode), complexity is 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

Upvotes: 0

Jon
Jon

Reputation: 437376

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

A better solution would also check that foos and bars have the same length, and that none of the elements are null as well. And of course, this example assumes that the collections are already sorted by Id.

Update:

So, here's the "better solution" in all its LINQy detail:

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

Upvotes: 3

Related Questions