In Part 1 of this series, I created a basic class that would find differences between two versions of the same list. I’ve made some improvements to the original implementation that I’d like to share.
To begin with, I read this post on the difference between the Any() and Count() extension methods. While List<T> does implement ICollection, I wanted to change the structure of DiffResult<T> so that if I decide to change the datatype of Additions, Deletions, and Modifications, the IsEmpty property will be more efficient. To that end, I changed IsEmpty as follows:
public bool IsEmpty { get { return !this.HasChanges; } } public bool HasChanges { get { return this.Additions.Any() || this.Deletions.Any() || this.Modifications.Any(); } }
The next thing I wanted to be able to do is automatically synchronize changes from the newlist back to the oldlist. Here is the unit test:
[Test] public void ResolveChangesBetweenTwoLists() { // Arrange: Declare any variables or set up any conditions // required by your test. var newVersion = new List<Entity>(); var oldVersion = new List<Entity>(); var identityComparer = new EntityIdentityComparer(); var modificationComparer = new EntityEqualityComparer(); var diff = new Diff<Entity>(identityComparer, modificationComparer); newVersion.Add(new Entity() { Id = 1, Name = "One"}); // addition newVersion.Add(new Entity() {Id =2, Name="2"}); // modification oldVersion.Add(new Entity() { Id = 2, Name = "Two"}); // modification oldVersion.Add(new Entity() { Id=3, Name="Three"}); // deletion // Act: Perform the activity under test. diff.Resolve(newVersion, oldVersion); var result = diff.Execute(newVersion, oldVersion); // Assert: Verify that the activity under test had the // expected results Assert.IsTrue(result.IsEmpty); // all changes should have been resolved. }
My first stab at the implementation is as follows:
public class Diff<T> { public void Resolve(IList<T> newVersion, IList<T> oldVersion) { var results = this.Execute(newVersion, oldVersion); foreach (var element in results.Deletions) oldVersion.Remove(element); foreach (var element in results.Additions) oldVersion.Add(element); foreach (var element in results.Modifications) { var keyItem = element; var item = oldVersion.First(e => _identityComparer.Equals(e, keyItem)); var index = oldVersion.IndexOf(item); oldVersion[index] = element; } }
…snipped
}
This implementation is fine as far as it goes, but there is a drawback that I don’t like. If the user of this codes wishes to determine if there are changes prior to executing the Resolve method, s/he is required to execute it a second time during the Resolve step. I like that placing Resolve() on the Diff class provides a single-step execution, so I’m going to move the real work to the DiffResult class, but leave the Resolve method where it is. I changed the implementation of Diff.Resolve() to this:
public void Resolve(IList<T> newVersion, IList<T> oldVersion) { var results = this.Execute(newVersion, oldVersion); results.Resolve(oldVersion, this._identityComparer); }
I added DiffResult.Resolve() as follows:
public void Resolve(IList<T> oldVersion, IEqualityComparer<T> identityComparer) { this.Deletions.ForEach(e => oldVersion.Remove(e)); this.Additions.ForEach(oldVersion.Add); this.Modifications.ForEach( e => { var item = oldVersion.First(element => identityComparer.Equals(element, e)); var index = oldVersion.IndexOf(item); oldVersion[index] = e; } ); }
The updated source code for this solution can be found here.