using System; using System.Collections.Generic; using System.Linq; public interface IIdComparer<T> { bool IdEquals(T a, T b); } public interface IContentComparer<T> { bool ContentEquals(T a, T b); } public class Change<T> { public Change(T left, T right) { Left = left; Right = right; } public T Left { get; } public T Right { get; } } public class SyncHelper { public static IEnumerable<T> GetNew<T>(IEnumerable<T> left, IEnumerable<T> right, IIdComparer<T> comparer) { return right.Where(x => !left.Any(y => comparer.IdEquals(x, y))); } public static IEnumerable<Change<T>> GetChanges<T>(IEnumerable<T> left, IEnumerable<T> right, IIdComparer<T> comparer, IContentComparer<T> contentComparer) { return right.Aggregate(new List<Change<T>>(), (List<Change<T>> items, T x) => { var leftItem = left.Where(y => comparer.IdEquals(x, y)).FirstOrDefault(); if (leftItem != null && !contentComparer.ContentEquals(leftItem, x)) { items.Add(new Change<T>(leftItem, x)); } return items; }); } public static IEnumerable<T> GetChanged<T>(IEnumerable<T> left, IEnumerable<T> right, IIdComparer<T> comparer, IContentComparer<T> contentComparer) { return right.Aggregate(new List<T>(), (List<T> items, T x) => { var leftItem = left.Where(y => comparer.IdEquals(x, y)).FirstOrDefault(); if (leftItem != null && !contentComparer.ContentEquals(leftItem, x)) { items.Add(x); } return items; }); } public static IEnumerable<T> GetUnchanged<T>(IEnumerable<T> left, IEnumerable<T> right, IIdComparer<T> comparer, IContentComparer<T> contentComparer) { return right.Aggregate(new List<T>(), (List<T> items, T x) => { var leftItem = left.Where(y => comparer.IdEquals(x, y)).FirstOrDefault(); if (leftItem != null && contentComparer.ContentEquals(leftItem, x)) { items.Add(x); } return items; }); } public static IEnumerable<T> GetDeleted<T>(IEnumerable<T> left, IEnumerable<T> right, IIdComparer<T> comparer) { return GetNew(right, left, comparer); } } public class Card { public Card(string id, string content) { Id = id; Content = content; } public string Id { get; set; } public string Content { get; set; } } public class CardComparer : IIdComparer<Card>, IContentComparer<Card> { public bool ContentEquals(Card a, Card b) { return a.Content == b.Content; } public bool IdEquals(Card a, Card b) { return a.Id == b.Id; } } class Program { public static IEnumerable<TResult> FullOuterJoinIterator<TLeft, TRight, TKey, TResult>( IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKeySelector, Func<TRight, TKey> rightKeySelector, Func<TLeft, TRight, TKey, TResult> resultSelector, IEqualityComparer<TKey> comparer, TLeft defaultLeft, TRight defaultRight) { var leftLookup = left.ToLookup(leftKeySelector, comparer); var rightLookup = right.ToLookup(rightKeySelector, comparer); var keys = leftLookup.Select(g => g.Key).Union(rightLookup.Select(g => g.Key), comparer); foreach (var key in keys) foreach (var leftValue in leftLookup[key].DefaultIfEmpty(defaultLeft)) foreach (var rightValue in rightLookup[key].DefaultIfEmpty(defaultRight)) yield return resultSelector(leftValue, rightValue, key); } static void Main(string[] args) { var left = new List<Card>() { new Card("1", "A"), new Card("2", "B"), new Card("3", "C"), new Card("4", "D"), new Card("5", "E"), }; var right = new List<Card>() { new Card("6", "A"), new Card("2", "B"), new Card("3", "E"), new Card("5", "F"), new Card("7", "A") }; var compared = FullOuterJoinIterator(left, right, x => x.Id, x => x.Id, (left, right, key) => new { Left = left, Right = right }, StringComparer.OrdinalIgnoreCase, null, null); foreach (var item in compared) { var differs = item.Left != null && item.Right != null && item.Left?.Content != item.Right?.Content ? "(differs)" : ""; Console.WriteLine($"{item.Left?.Id ?? "_":12} <=> {item.Right?.Id ?? "_":12} {differs}"); } foreach (var item in SyncHelper.GetNew(left, right, new CardComparer())) { Console.WriteLine($"new: {item.Id}"); } Console.WriteLine($"================="); foreach (var change in SyncHelper.GetChanges(left, right, new CardComparer(), new CardComparer())) { Console.WriteLine($"change: {change.Left.Id}, '{change.Left.Content}' => '{change.Right.Content}'"); } Console.WriteLine($"================="); foreach (var item in SyncHelper.GetDeleted(left, right, new CardComparer())) { Console.WriteLine($"deleted: {item.Id}"); } Console.WriteLine($"================="); foreach (var item in SyncHelper.GetChanged(left, right, new CardComparer(), new CardComparer())) { Console.WriteLine($"changed: {item.Id} '{item.Content}'"); } Console.WriteLine($"================="); foreach (var item in SyncHelper.GetUnchanged(left, right, new CardComparer(), new CardComparer())) { Console.WriteLine($"unchanged: {item.Id} '{item.Content}'"); } Console.WriteLine($"================="); Console.WriteLine("Finished."); } } // new: 6 // new: 7 // ================= // change: 3, 'C' => 'E' // change: 5, 'E' => 'F' // ================= // deleted: 1 // deleted: 4 // ================= // changed: 3 'E' // changed: 5 'F' // ================= // unchanged: 2 'B' // ================= // Finished.
395200cookie-checkC# Synchronisation functions