1

In C# .NET 4.0, I am struggling to come up with the most efficient way to determine if the contents of 2 lists of items contain any differences.

I don't need to know what the differences are, just true/false whether the lists are different based on my criteria.

The 2 lists I am trying to compare contain FileInfo objects, and I want to compare only the FileInfo.Name and FileInfo.LastWriteTimeUtc properties of each item. All the FileInfo items are for files located in the same directory, so the FileInfo.Name values will be unique.

To summarize, I am looking for a single Boolean result for the following criteria:

  1. Does ListA contain any items with FileInfo.Name not in ListB?
  2. Does ListB contain any items with FileInfo.Name not in ListA?
  3. For items with the same FileInfo.Name in both lists, are the FileInfo.LastWriteTimeUtc values different?

Thank you,

Kyle

KyleK
  • 190
  • 13
  • This looks like a duplicate of http://stackoverflow.com/q/945158/1814576, plus using a custom IComparer. – Markus Ende Oct 07 '15 at 14:06
  • The `FileInfo.Name` is not unique, have you considered that? So you might have names in both lists which appear more than once with different times. That applies to your third criterium. Are you referring to the `FullName` property? – Tim Schmelter Oct 07 '15 at 14:14
  • @TimSchmelter The FileInfo items being compared are for files in the same directory, so the FileInfo.Name will be unique in this case. I have updated my question to clarify this. – KyleK Oct 07 '15 at 14:34
  • @KyleK: it would be better to use the `FullName` anyway in case you will include sub-directories (or other directories) in future. – Tim Schmelter Oct 07 '15 at 14:47

2 Answers2

1

I would use a custom IEqualityComparer<FileInfo> for this task:

public class FileNameAndLastWriteTimeUtcComparer : IEqualityComparer<FileInfo>
{
    public bool Equals(FileInfo x, FileInfo y)
    {
        if(Object.ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        return x.FullName.Equals(y.FullName) && x.LastWriteTimeUtc.Equals(y.LastWriteTimeUtc);
    }

    public int GetHashCode(FileInfo fi)
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            hash = hash * 23 + fi.FullName.GetHashCode();
            hash = hash * 23 + fi.LastWriteTimeUtc.GetHashCode();
            return hash;
        }
    }
}

Now you can use a HashSet<FileInfo> with this comparer and HashSet<T>.SetEquals:

var comparer = new FileNameAndLastWriteTimeUtcComparer();
var uniqueFiles1 = new HashSet<FileInfo>(list1, comparer);
bool anyDifferences = !uniqueFiles1.SetEquals(list2);

Note that i've used FileInfo.FullName instead of Name since names aren't unqiue at all.

Sidenote: another advantage is that you can use this comparer for many LINQ methods like GroupBy, Except, Intersect or Distinct.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
0

This is not the most efficient way (probably ranks a 4 out of 5 in the quick-and-dirty category):

var comparableListA = ListA.Select(a => 
    new { Name = a.Name, LastWrite = a.LastWriteTimeUtc, Object = a});
var comparableListB = ListB.Select(b => 
    new { Name = b.Name, LastWrite = b.LastWriteTimeUtc, Object = b});

var diffList = comparableListA.Except(comparableListB);

var youHaveDiff = diffList.Any();

Explanation:
Anonymous classes are compared by property values, which is what you're looking to do, which led to my thinking of doing a LINQ projection along those lines.

P.S.
You should double check the syntax, I just rattled this off without the compiler.

code4life
  • 15,655
  • 7
  • 50
  • 82