views:

46

answers:

2

i have what seems like a common problem / pattern. two collections of the same object. The object has a number of properties and some nested objects within it. Car has a property called id which is the unique identifier.

I want to find the LINQ way to do a diff, which includes:

  1. Items in one collection and not the other (visa versa)
  2. For the items that match, are there any changes (changes would be a comparison of all properties? (i only care about settable properties, would i use reflection for this ?? )
+5  A: 

You can use the Enumerable.Except() method. This uses a comparer (either a default or one you supply) to evaluate which objects are in both sequences or just one:

var sequenceA = new[] { "a", "e", "i", "o", "u" };
var sequenceB = new[] { "a", "b", "c" };

var sequenceDiff = sequenceA.Except( sequenceB );

If you want to perform a complete disjunction of both sequences (A-B) union (B-A), you would have to use:

var sequenceDiff = 
         sequenceA.Except( sequenceB ).Union( sequenceB.Except( sequenceA ) );

If you have a complex type, you can write an IComparer<T> for your type T and use the overload that accepts the comparer.

For the second part of your question, you would need to roll your own implementation to report which properties of a type are different .. there's nothing built into the .NET BCL directly. You have to decide what form this reporting would take? How would you identify and express differences in a complex type? You could certainly use reflection for this ... but if you're only dealing with a single type I would avoid that, and write a specialized differencing utility just for it. If yo're going to support a borad range of types, then reflection may make more sense.

LBushkin
+1  A: 

You've already received an excellent answer for your first half. The second half, as LBushkin explains, cannot be done by BCL classes directly. Here's a simple method that goes through all public settable properties (note: it is possible that the gettor, in these cases, is not public!) and compares them one by one. If two objects are 100% equal, it will return true. Else, it will break out early and return false:

static bool AllSettablePropertiesEqual<T>(T obj1, T obj2)
{
    PropertyInfo[] info1 = obj1.GetType().GetProperties(
        BindingFlags.Public |
        BindingFlags.SetProperty |
        BindingFlags.Instance);      // get public properties

    PropertyInfo[] info2 = obj2.GetType().GetProperties(
        BindingFlags.Public |
        BindingFlags.SetProperty |
        BindingFlags.Instance);      // get public properties

    // a loop is easier than linq here, and we can break out quick:
    for (var i = 0; i < info1.Length; i++)
    {
        var value1 = info1[i].GetValue(obj1, null);
        var value2 = info2[i].GetValue(obj2, null)
        if(value1 == null || value2 ==null)
        {
            if(value1 != value2)
                return false;
        }
        else if (!value1.Equals(value2))
        {
            return false;
        }
    }
    return true;
}

You could easily add this method to a standard LINQ expression, like this:

var reallyReallyEqual = from itemA in listA
                        join itemB in listB 
                          on AllSettablePropertiesEqual(itemA, itemB)
                        select itemA;
Abel
@Abel - the issue is that i want the opposite. I want a list of all items that are the "same" based on the IEqualityComparer but have different properties. I basically want a list of items with all the field diffs
ooo
@ooo: To find the differences, use the same approach, but change the join-statement by putting an `!` in front of `AllSettableProperties`. It returns a list of items that are different. To find the differences themselves, you can use the reflection example above as well, just change the method to return a list of Pairs with the differences.
Abel