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