Question

Suppose there are these two strongly typed lists:

List 1 : existingitems

ID, Name, Cat
1, ABC, C
2, BCD, D
3, NNN, F

List 2 : newitems

ID, Name, Cat
9, ABC, C
15, BCD, D
12, NNN, F

Basically, I want to check that the Name and Cat values are the same in both lists. If the two lists are identical on these two columns, return true, otherwise false.

I'd tried a few variations mostly around the below but always seems to return true, even is the newitems list has a new row, which I would expect to return false.

newitems.Any(x1 => existingitems.All(x2 => (x1.Name== x2.Name) && (x1.Cat== x2.Cat)));
Was it helpful?

Solution

I believe this is probably the cleanest and simplest solution for you.

var list1Subset = list1.Select(i => new {i.Name, i.Cat});
var list2Subset = list2.Select(i => new {i.Name, i.Cat});

bool equal = list1Subset.SequenceEqual(list2Subset);

OTHER TIPS

You can do this efficiently with a HashSet and custom comparer:

public class ItemComparer : IEqualityComparer<Item>
{
    public bool Equals(Item x, Item y)
    {
        return (x.Cat == y.Cat) && (x.Name == y.Name);
    }

    public int GetHashCode(Item obj)
    {
        return (obj.Cat.GetHashCode() * 17) + (obj.Name.GetHashCode() * 17);
    }
}

public bool AreEqual(IEnumerable<T> set1, IEnumerable<T> set2, 
    IEqualityComparer<T> equalityComparer)
{
    // Handle cheapest cases
    if (set1 == null && set2 == null)
    {
        return true;
    }
    else if (set1 == null && set2 != null
        || set1 != null && set2 == null)
    {
        return false;
    }
    else if (object.ReferenceEquals(set1, set2))
    {
        return true;
    }

    var hashSet1 = new HashSet<T>(set1, equalityComparer);
    var hashSet2 = new HashSet<T>(set2, equalityComparer);

    // More easy cases
    if (hashSet1.Count != hashSet2.Count)
    {
        return false;
    }

    if (set1.Any(i => !hashSet2.Contains(i))
        || set2.Any(i => !hashSet1.Contains(i)))
    {
        return false;
    }

    return true;
}
var areEqual = !existingitems.Select(x => new { x.Name, x, Cat })
                       .Except(newItems.Select(x => new { x.Name, x, Cat }))
                       .Any() 
             && !newItems.Select(x => new { x.Name, x, Cat })
                       .Except(existingitems.Select(x => new { x.Name, x, Cat }))
                       .Any();

or

var areEqual
     = newItems.Select(x => new { x.Name, x, Cat })
               .OrderBy(x => x.Name)
               .ThenBy(x => x.Cat)
               .SequanceEquals(existingitems.Select(x => new { x.Name, x, Cat })
                                       .OrderBy(x => x.Name)
                                       .ThenBy(x => x.Cat));

We can first define a method to determine if two sequences are equal sets:

public static bool SetEquals<T>(this IEnumerable<T> first
    , IEnumerable<T> second
    , IEqualityComparer<T> comparer = null)
{
    comparer = comparer ?? EqualityComparer<T>.Default;
    return new HashSet<T>(second).SetEquals(first);
}

Then we can filter out the fields that you want and perform the set equality on that projection:

var areEqual = list1.Select(item => new{item.Name, item.Cat})
    .SetEquals(list2.Select(item => new{item.Name, item.Cat}));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top