{"id":3952,"date":"2020-08-25T10:56:34","date_gmt":"2020-08-25T09:56:34","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=3952"},"modified":"2022-07-25T08:32:08","modified_gmt":"2022-07-25T07:32:08","slug":"c-synchronisation-functions","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/c-synchronisation-functions\/","title":{"rendered":"C# Synchronisation functions"},"content":{"rendered":"\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic interface IIdComparer&lt;T>\n{\n    bool IdEquals(T a, T b);\n}\n\npublic interface IContentComparer&lt;T>\n{\n    bool ContentEquals(T a, T b);\n}\n\npublic class Change&lt;T>\n{\n    public Change(T left, T right)\n    {\n        Left = left;\n        Right = right;\n    }\n    public T Left { get; }\n    public T Right { get; }\n}\n\npublic class SyncHelper\n{\n    public static IEnumerable&lt;T> GetNew&lt;T>(IEnumerable&lt;T> left, IEnumerable&lt;T> right, IIdComparer&lt;T> comparer)\n    {\n        return right.Where(x => !left.Any(y => comparer.IdEquals(x, y)));\n    }\n\n    public static IEnumerable&lt;Change&lt;T>> GetChanges&lt;T>(IEnumerable&lt;T> left, IEnumerable&lt;T> right, IIdComparer&lt;T> comparer, IContentComparer&lt;T> contentComparer)\n    {\n        return right.Aggregate(new List&lt;Change&lt;T>>(), (List&lt;Change&lt;T>> items, T x) =>\n        {\n            var leftItem = left.Where(y => comparer.IdEquals(x, y)).FirstOrDefault();\n            if (leftItem != null &amp;&amp; !contentComparer.ContentEquals(leftItem, x))\n            {\n                items.Add(new Change&lt;T>(leftItem, x));\n            }\n            return items;\n        });\n    }\n\n    public static IEnumerable&lt;T> GetChanged&lt;T>(IEnumerable&lt;T> left, IEnumerable&lt;T> right, IIdComparer&lt;T> comparer, IContentComparer&lt;T> contentComparer)\n    {\n        return right.Aggregate(new List&lt;T>(), (List&lt;T> items, T x) =>\n        {\n            var leftItem = left.Where(y => comparer.IdEquals(x, y)).FirstOrDefault();\n            if (leftItem != null &amp;&amp; !contentComparer.ContentEquals(leftItem, x))\n            {\n                items.Add(x);\n            }\n            return items;\n        });\n    }\n\n    public static IEnumerable&lt;T> GetUnchanged&lt;T>(IEnumerable&lt;T> left, IEnumerable&lt;T> right, IIdComparer&lt;T> comparer, IContentComparer&lt;T> contentComparer)\n    {\n        return right.Aggregate(new List&lt;T>(), (List&lt;T> items, T x) =>\n        {\n            var leftItem = left.Where(y => comparer.IdEquals(x, y)).FirstOrDefault();\n            if (leftItem != null &amp;&amp; contentComparer.ContentEquals(leftItem, x))\n            {\n                items.Add(x);\n            }\n            return items;\n        });\n    }\n\n    public static IEnumerable&lt;T> GetDeleted&lt;T>(IEnumerable&lt;T> left, IEnumerable&lt;T> right, IIdComparer&lt;T> comparer)\n    {\n        return GetNew(right, left, comparer);\n    }\n}\n\npublic class Card\n{\n    public Card(string id, string content)\n    {\n        Id = id;\n        Content = content;\n    }\n\n    public string Id { get; set; }\n    public string Content { get; set; }\n}\n\npublic class CardComparer : IIdComparer&lt;Card>, IContentComparer&lt;Card>\n{\n    public bool ContentEquals(Card a, Card b)\n    {\n        return a.Content == b.Content;\n    }\n\n    public bool IdEquals(Card a, Card b)\n    {\n        return a.Id == b.Id;\n    }\n}\n\nclass Program\n{\n    public static IEnumerable&lt;TResult> FullOuterJoinIterator&lt;TLeft, TRight, TKey, TResult>(\n        IEnumerable&lt;TLeft> left,\n        IEnumerable&lt;TRight> right,\n        Func&lt;TLeft, TKey> leftKeySelector,\n        Func&lt;TRight, TKey> rightKeySelector,\n        Func&lt;TLeft, TRight, TKey, TResult> resultSelector,\n        IEqualityComparer&lt;TKey> comparer,\n        TLeft defaultLeft,\n        TRight defaultRight)\n    {\n        var leftLookup = left.ToLookup(leftKeySelector, comparer);\n        var rightLookup = right.ToLookup(rightKeySelector, comparer);\n        var keys = leftLookup.Select(g => g.Key).Union(rightLookup.Select(g => g.Key), comparer);\n\n        foreach (var key in keys)\n            foreach (var leftValue in leftLookup[key].DefaultIfEmpty(defaultLeft))\n                foreach (var rightValue in rightLookup[key].DefaultIfEmpty(defaultRight))\n                    yield return resultSelector(leftValue, rightValue, key);\n    }\n\n    static void Main(string[] args)\n    {\n        var left = new List&lt;Card>()\n            {\n                new Card(\"1\", \"A\"),\n                new Card(\"2\", \"B\"),\n                new Card(\"3\", \"C\"),\n                new Card(\"4\", \"D\"),\n                new Card(\"5\", \"E\"),\n            };\n\n        var right = new List&lt;Card>()\n            {\n                new Card(\"6\", \"A\"),\n                new Card(\"2\", \"B\"),\n                new Card(\"3\", \"E\"),\n                new Card(\"5\", \"F\"),\n                new Card(\"7\", \"A\")\n            };\n\n        var compared = FullOuterJoinIterator(left, right, x => x.Id, x => x.Id, (left, right, key) => new { Left = left, Right = right }, StringComparer.OrdinalIgnoreCase, null, null);\n        foreach (var item in compared)\n        {\n            var differs = item.Left != null &amp;&amp; item.Right != null &amp;&amp; item.Left?.Content != item.Right?.Content ? \"(differs)\" : \"\";\n            Console.WriteLine($\"{item.Left?.Id ?? \"_\":12} &lt;=> {item.Right?.Id ?? \"_\":12} {differs}\");\n        }\n\n        foreach (var item in SyncHelper.GetNew(left, right, new CardComparer()))\n        {\n            Console.WriteLine($\"new: {item.Id}\");\n        }\n\n        Console.WriteLine($\"=================\");\n\n        foreach (var change in SyncHelper.GetChanges(left, right, new CardComparer(), new CardComparer()))\n        {\n            Console.WriteLine($\"change: {change.Left.Id}, '{change.Left.Content}' => '{change.Right.Content}'\");\n        }\n\n        Console.WriteLine($\"=================\");\n\n        foreach (var item in SyncHelper.GetDeleted(left, right, new CardComparer()))\n        {\n            Console.WriteLine($\"deleted: {item.Id}\");\n        }\n\n        Console.WriteLine($\"=================\");\n\n        foreach (var item in SyncHelper.GetChanged(left, right, new CardComparer(), new CardComparer()))\n        {\n            Console.WriteLine($\"changed: {item.Id} '{item.Content}'\");\n        }\n\n        Console.WriteLine($\"=================\");\n\n        foreach (var item in SyncHelper.GetUnchanged(left, right, new CardComparer(), new CardComparer()))\n        {\n            Console.WriteLine($\"unchanged: {item.Id} '{item.Content}'\");\n        }\n\n        Console.WriteLine($\"=================\");\n        Console.WriteLine(\"Finished.\");\n    }\n}\n\n\/\/ new: 6\n\/\/ new: 7\n\/\/ =================\n\/\/ change: 3, 'C' => 'E'\n\/\/ change: 5, 'E' => 'F'\n\/\/ =================\n\/\/ deleted: 1\n\/\/ deleted: 4\n\/\/ =================\n\/\/ changed: 3 'E'\n\/\/ changed: 5 'F'\n\/\/ =================\n\/\/ unchanged: 2 'B'\n\/\/ =================\n\/\/ Finished.\n<\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[6,4,1],"tags":[],"class_list":["post-3952","post","type-post","status-publish","format-standard","hentry","category-dotnet","category-programming","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/3952","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=3952"}],"version-history":[{"count":3,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/3952\/revisions"}],"predecessor-version":[{"id":3957,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/3952\/revisions\/3957"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=3952"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=3952"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=3952"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}