Matas Vaitkevicius
Matas Vaitkevicius

Reputation: 61401

Comparing two collections with IEquatable while using only LINQ

This question is for educational purposes only and I can solve it easily by using for loop that returns false on first mismatch.

I am implementing IEquatable<CustomerFeedbackViewModel> on CustomerFeedbackViewModel which has a collection of QuestionViewModel that I need to compare element by element.

public class CustomerFeedbackViewModel 
{
    public List<QuestionViewModel> Questions { get; set; }

    public string PageName { get; set; }

    public string SessionId { get; set; }
}

when implementing Equals instead of using for loop I mentioned above I wanted to use TrueForAll method it would look something like below.

public bool Equals(CustomerFeedbackViewModel other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Questions.TrueForAll((o,i) => o.Equals(other.Questions.ElementAt(i))) && string.Equals(PageName, other.PageName) && string.Equals(SessionId, other.SessionId);
    }

Ofc TrueForAll does not have index and above will never fly.

How could I go around implementing comparison of two lists without using for loop but instead using linq 'oneliner'?

Upvotes: 5

Views: 3022

Answers (1)

Tim Schmelter
Tim Schmelter

Reputation: 460108

Instead of comparing each question at all indexes you should use Enumerable.SequenceEqual:

return string.Equals(PageName, other.PageName) 
    && string.Equals(SessionId, other.SessionId) 
    && Questions.SequenceEqual(other.Questions);

If you don't override Equals + GethashCode in QuestionViewModel you can provide a custom IEqualityComparer<QuestionViewModel> and pass that to the SequenceEqual overload or implement IEquatable<QuestionViewModel>.

Edit: In general you should initialize list properties, for example via constructor or auto implemented properties. Othewise you can get null reference exceptions everywhere, for example in Equals. So a safer Equals implementation would be:

public bool Equals(CustomerFeedbackViewModel other)
{
    if (ReferenceEquals(null, other)) 
        return false;
    if (ReferenceEquals(this, other)) 
        return true;

    return string.Equals(PageName, other.PageName) 
        && string.Equals(SessionId, other.SessionId) 
        && Questions?.SequenceEqual(other.Questions) == true;
}

Upvotes: 11

Related Questions