Leedan Johnson
Leedan Johnson

Reputation: 311

C# List of different object types: Better way than Zip to check equality

I have code that works right now, effectively performing the elementwise comparison in the lambda for Linq's Zip extension method, then checking for any failed matches, but I'm nearly certain there must be a more concise and readable way of accomplishing the same thing.

I know I could implement an EqualityComparer and use SequenceEqual, but I dislike the overhead of that solution for one-off list comparisons like this one. I'm willing to accept that an EqualityComparer is the correct solution, but if there are alternatives, I'd love to see them.

class Occurrence {
    DateTime Start;
    // More fields...
}

bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
    if (occurrences.Count != selectedDates.Count)
            return false;

    // These three lines are the focus of the question
    var dateEqualList = occurrences.Zip(selectedDates, (a, b) => a.Start.Date == b.Date);
    if (dateEqualList.Contains(false))
        return false;

    return true;
}

Upvotes: 1

Views: 282

Answers (3)

Harald Coppoolse
Harald Coppoolse

Reputation: 30454

So you want a LINQ statement that checks if Occurrences[i].Start equals selectedDates[i] for all indexes. You want to stop as soon as you've found an unequal sequence.

My advice would be to first Select property Start, and then check if the resulting sequence equals the selectedDates:

List<Occurence> occurences = ...
List<DateTime> selectedDates = ...

var allDatesEqual = occurrences.Count == selectedDates.Count
                 && occurrences.Select(occurrence => occurence.Start)
                               .SequenceEqual(selectedDates);

Normally SequenceEqual will check the length if both sequences implement ICollection. However the result of the Select does not implement ICollection, hence for this optimization you'll need to check the Count yourself.

I read that you order the sequences before you start comparing. If all dates are unique, consider to use a HashSet<DateTime>

List<Occurence> occurences = ...
HashSet<DateTime> datesSet = new HashSet<DateTime>(selectedDates);

var allDatesEqual = datesSet.SetEquals(occurrences
                                       .Select(occurrence => occurence.Start));

SetEquals returns true if they contain exactly the same elements, ignoring the order, so set {A, B, C} equals set {B, C, A}, but not equal to {A, B}, nor {A, B, C, D}

Upvotes: 1

denys-vega
denys-vega

Reputation: 3697

I wrote two different alternatives, the second one have much better performance.

Alternative 1:

bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
    if (occurrences.Count != selectedDates.Count)
        return false;

    var matchCount = occurrences.TakeWhile((d, i) => d.Start.Date == selectedDates[i].Date).Count();
    return matchCount == occurrences.Count;
}

Alternative 2:

bool SameDates2(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
    if (occurrences.Count != selectedDates.Count)
        return false;

    for (var i = 0; i < occurrences.Count; i++)
    {
        if (occurrences[i].Start.Date != selectedDates[i].Date)
            return false;
    }

    return true;
}

Upvotes: 2

John
John

Reputation: 96

How big will these lists be? Would it be possible to take another approach and do something like this:

class Occurrence {
    DateTime Start;
    // More fields...
}

bool SameDates(List<Occurrence> occurrences, List<DateTime> selectedDates)
{
    if (occurrences.Count != selectedDates.Count)
            return false;
    
    var dates1List = occurrences.Select(o => o.Start).Distinct();
    var dates1String = string.Join(",", dates1List.ToArray());
    
    var dates2List = selectedDates.Select(o => o.Start).Distinct();
    var dates2String = string.Join(",", dates2List.ToArray());
    
    return dates1String == dates2String;
}

Even if this doesn't work exactly or fit your problem, maybe it will give you some ideas.

Upvotes: 0

Related Questions