Rg.DiffUtils is a powerful tool that allows to merge collections and find differences.
The library uses Eugene W. Myers's algorithm to find differences and runs the second pass to find movements.
The difference between this library and the rest that this library expands the ObservableCollection and allows to notify UI or other elements about all changes in collections.
The library can be used in all platforms which support .NET Standard 1.0 or newer.
See more: https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support
In most cases, you can use the DiffObservableCollection for merging collections.
The class provides the ReplaceDiff method to merge a new collection into existing.
var array1 = new int[] { 1, 2, 3, 4, 5 };
var array2 = new int[] { 4, 5, 1, 6, 7 };
var collection = new DiffObservableCollection<int>(array1);
collection.ReplaceDiff(array2);To transform array1 to array2 the library runs 3 steps:
- Add
6and7to 5 and 6 positions - Remove
2and3from 1 and 2 positions - Move
1from 0 to 2 position
As you can see the library tries to optimize manipulations and batch several operations to one step. You can change this behavior in DiffOptions
However, there are times when you can't use the DiffObservableCollection. For example, when you use your own implementation of the IEnumerable.
In this case, you can use the DiffUtil to calculate all steps for merging collections.
var array1 = new YourOwnCollection();
var array2 = new int[] { 4, 5, 1, 6, 7 };
var result = DiffUtil.CalculateDiff(array1, array2);DiffUtil.CalculateDiff returns DiffResult. You can use the Steps property to mutate your own collection step by step.
DiffUtil.CalculateDiff, DiffObservableCollection.ReplaceDiff and the DiffObservableCollection constructor can take an optional class the DiffOptions
var options = new DiffOptions
{
AllowBatching = true, //Default: true. If it's false, all steps contain only one item even if items follow each other
DetectMoves = true //Default: true. If it's false, the second pass for movements detection doesn't work
};By default DiffUtil.CalculateDiff uses EqualityComparer<T>.Default for comparing items but you can implement your own comparator.
For this case create DiffEqualityComparer which takes IEqualityComparer or a delegate in the constructor.
var diffComparer = new DiffEqualityComparer<int>(new YourEqualityComparer());
// or
var diffComparer = new DiffEqualityComparer<int>((int oldItem, int newItem) => oldItem == newItem);or implement IDiffEqualityComparer
class MyDiffEqualityComparer : IDiffEqualityComparer<int>
{
public bool CompareItems(int oldItem, int newItem)
{
return oldItem == newItem;
}
}IDiffEqualityComparer can be used in DiffUtil.CalculateDiff but in DiffObservableCollection you have to use IDiffHandler
Also, you can use ToDiffEqualityComparer extension method to convert IEqualityComparer to DiffEqualityComparer
IDiffHandler inherits from IDiffEqualityComparer and contains a new method UpdateItem.
The difference between IDiffHandler and IDiffEqualityComparer that the IDiffHandler updates equaled elements that allows applying new data to an existing item.
class MyDiffHandler : IDiffHandler<MyCustomClass>
{
public bool CompareItems(MyCustomClass oldItem, MyCustomClass newItem)
{
return oldItem.Id == newItem.Id;
}
public void UpdateItem(MyCustomClass oldItem, MyCustomClass newItem)
{
// this method is invoked for each item if CompareItems was true
if (oldItem.Name != newItem.Name)
oldItem.Name = newItem.Name;
}
}If you don't want to implement IDiffHandler you can use DiffHandler which takes IDiffEqualityComparer, IEqualityComparer, or a delegate as the first parameter, and an optional delegate which updates items as the second parameter
var array = new int[] { 4, 5, 1, 6, 7 };
var collection = new DiffObservableCollection<int>();
collection.ReplaceDiff(array, new DiffHandler<int>(new YourDiffEqualityComparer(), UpdateItems /*optional*/);
//or
collection.ReplaceDiff(array, new DiffHandler<int>(new YourEqualityComparer(), UpdateItems /*optional*/);
//or
collection.ReplaceDiff(array, new DiffHandler<int>((int x, int y) => x == y, UpdateItems /*optional*/);
void UpdateItems(int oldItem, int newItem)
{
// update oldItem here
}You can use the ToDiffHandler extension method to convert IEqualityComparer or IDiffEqualityComparer to DiffHandler
DiffResult contains all information about merging the first collection to the second.
T[] OldSequenceThe old collection converted to ArrayT[] NewSequenceThe new collection converted to ArrayIReadOnlyList<DiffItem<T>> SameItemsMovedItems and NotMovedItems itemsIReadOnlyList<DiffItem<T>> MovedItemsAll moved same itemsIReadOnlyList<DiffItem<T>> NotMovedItemsAll not moved same itemsIReadOnlyList<DiffItem<T>> RemovedItemsAll items which were removedIReadOnlyList<DiffItem<T>> AddedItemsAll items which were addedIReadOnlyList<DiffStep<T>> StepsAll the necessary steps to merge the new collection to the old
