0

Lets say you want to compare two lists of objects based on a truncated (I think this is the word) version of a DateTime property:

public class MyObject 
{
    public DateTime SomeDate {get;set;}
}

Compare lists normally:

bool didItWork = myFirstList.Intersect(MySecondList).Any();

All fine and good here except I need to do this check based on the above DateTime property:

bool didItWork = myFirstLIst.Intersect(MySecondList).Any(someIntuitivePredicate);

As you can see I don't know what the intuitive predicate is.

And for a kicker I want to cut the Time section off of SomeDate when I do the compare:

dateWithoutTime = myObject.SomeDate.Date;

The time is not important just the actual date.

Avram
  • 4,267
  • 33
  • 40
Robert
  • 4,306
  • 11
  • 45
  • 95
  • in this case rather easy: use `.Select` to map to the `DateTime`/date ;9 – Random Dev Apr 08 '15 at 19:40
  • Is your goal only to make sure at least one item matches? Or do you want to make sure all items have a match? **On both sides?** – AaronLS Apr 08 '15 at 19:50
  • The distinction is important, some of the below answers only make sure there is at least one matching item in mySecondList, but not the other way around in that they allow mySecondList to have extra items that aren't in first. So it's important to describe carefully what you want. – AaronLS Apr 08 '15 at 19:52
  • @AaronLS If there are any items in either list that do not exist in the other – Robert Apr 08 '15 at 19:54
  • @Robert Hmm, the answer you marked as correct will return true if only one item in the list matches. So there could be many items that do not exist in each list, but it will still return true if just one matches. – AaronLS Apr 08 '15 at 20:04

3 Answers3

1

As far as I can see, you have two options:

  1. Implement a custom IEqualityComparer and pass it to the Intersect overload that takes an IEqualityComparer.

Or

  1. Simulate Intersect with nested Any:

    bool didItWork = myFirstList.Any(item1 => 
                     mySecondList.Any(item2 =>
                     item1.SomeDate.Date == item2.SomeDate.Date));
    

    Obviously, item1.SomeDateDate == item2.SomeDate.Date can be replaced by an arbitrary predicate.

    Here's an alternative version for those who prefer the LINQ query syntax:

    bool didItWork = (from item1 in myFirstList
                      from item2 in mySecondList
                      where item1.SomeDate.Date == item2.SomeDate.Date).Any();
    
Heinzi
  • 167,459
  • 57
  • 363
  • 519
1

You can use an overload of the Intersect method that also takes an IEqualityComparer implementation as an input:

bool didItWork = myFirstList.Intersect(MySecondList, new MyEqualityComparer()).Any();

// ...

public class MyEqualityComparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.SomeDate.Date == y.SomeDate.Date;
    }

    public int GetHashCode(MyObject x)
    {
        return x.SomeDate.Date.GetHashCode();
    }
}
Markus
  • 20,838
  • 4
  • 31
  • 55
1

Seeing you unmarked the answer, I'm going to take a crack at this, however the answers really are good efforts, but a result of your vague problem definition. Even your comment "If there are any items in either list that do not exist in the other" is a little awkwardly worded. Not trying to grill you, but just keep that in mind. When dealing with set operations you have to take a step back and really think carefully about what you want to accomplish. I'm going to interpret your goal as this, and if this is incorrect, you should update your question with a more clearly defined problem definition:

I want to check and make sure that for every item in myFirstList, there is an item in mySecondList with the same SomeDate.Date, and vice versa.

If you are truncating time from a date, then I would speculate there might be cases where there are two items with the same date. If this were not the case, there are easier methods that take approaches such as joining or intersecting the lists and checking that the results have equal Count as the originals(which verifies that all items found a match). The other answers using join or intersect don't quite work, because they use .Any() which will return true if just one of the items matches, rather than all.

.All will make sure that all items in one list meet some criteria. In this case, make sure every item has a match in the second list.

bool isListEqual = myFirstList.All(x=> 
      mySecondList.Select(y=>y.SomeDate.Date).Contains( x.SomeDate.Date) )

isListEqual = isListEqual && mySecondList.All(x=> 
      myFirstList.Select(y=>y.SomeDate.Date).Contains( x.SomeDate.Date) )

We do it both directions to ensure there are no items in the second list without a matching item in the first.

If we knew there were no duplicates, another approach would be to join the lists and count the results to ensure they match the originals. Or we could simply eliminate duplicates to generate distinct lists. You can also use SetEquals of hashset, or SequenceEquals of IEnumerable. SequenceEquals is better if you know your results are already in a particular order.

   var firstDates = myFirstList.Select(l=>l.SomeDate.Date).Distinct().OrderBy(d=>d).ToList();
   var secondDates = mySecondList.Select(l=>l.SomeDate.Date).Distinct().OrderBy(d=>d).ToList();

   bool isListEqual = firstDates.SequenceEqual(secondDates);

I think the most important thing you should take from this, is the approach is dependent on what assumptions you can make about your input data.

This question covers several approaches, and the duplicate it is linked to as well:

Check if two lists are equal

Community
  • 1
  • 1
AaronLS
  • 37,329
  • 20
  • 143
  • 202