懒得勤快 9 月之前
父节点
当前提交
6752ed3309

+ 75 - 28
Masuit.Tools.Abstractions/Extensions/BaseType/IEnumerableExtensions.cs

@@ -1230,14 +1230,7 @@ public static class IEnumerableExtensions
     /// <returns></returns>
     public static (List<T> adds, List<T> remove, List<T> updates) CompareChanges<T, TKey>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, TKey> keySelector)
     {
-        first ??= new List<T>();
-        second ??= new List<T>();
-        var firstSource = first as ICollection<T> ?? first.ToList();
-        var secondSource = second as ICollection<T> ?? second.ToList();
-        var add = firstSource.ExceptBy(secondSource, keySelector).ToList();
-        var remove = secondSource.ExceptBy(firstSource, keySelector).ToList();
-        var update = firstSource.IntersectBy(secondSource, keySelector).ToList();
-        return (add, remove, update);
+        return CompareChanges(first, second, keySelector, keySelector);
     }
 
     /// <summary>
@@ -1252,14 +1245,30 @@ public static class IEnumerableExtensions
     [Obsolete("大数据集时性能不佳,请考虑使用其他重载")]
     public static (List<T1> adds, List<T2> remove, List<T1> updates) CompareChanges<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> condition)
     {
-        first ??= new List<T1>();
-        second ??= new List<T2>();
-        var firstSource = first as ICollection<T1> ?? first.ToList();
-        var secondSource = second as ICollection<T2> ?? second.ToList();
-        var add = firstSource.ExceptBy(secondSource, condition).ToList();
-        var remove = secondSource.ExceptBy(firstSource, (s, f) => condition(f, s)).ToList();
-        var update = firstSource.IntersectBy(secondSource, condition).ToList();
-        return (add, remove, update);
+        if (first == null) throw new ArgumentNullException(nameof(first));
+        if (second == null) throw new ArgumentNullException(nameof(second));
+        if (condition == null) throw new ArgumentNullException(nameof(condition));
+
+        var adds = new List<T1>();
+        var removes = new List<T2>();
+        var updates = new List<T1>();
+        var secondSet = new HashSet<T2>(second);
+        foreach (var item in first)
+        {
+            var match = secondSet.FirstOrDefault(s => condition(item, s));
+            if (match != null)
+            {
+                updates.Add(item);
+                secondSet.Remove(match);
+            }
+            else
+            {
+                adds.Add(item);
+            }
+        }
+
+        removes.AddRange(secondSet);
+        return (adds, removes, updates);
     }
 
     /// <summary>
@@ -1277,11 +1286,30 @@ public static class IEnumerableExtensions
     {
         first ??= new List<T1>();
         second ??= new List<T2>();
+
         var firstLookup = first.ToLookup(firstKeySelector);
         var secondLookup = second.ToLookup(secondKeySelector);
-        var adds = firstLookup.Where(kvp => !secondLookup.Contains(kvp.Key)).SelectMany(kvp => kvp).ToList();
-        var removes = secondLookup.Where(kvp => !firstLookup.Contains(kvp.Key)).SelectMany(kvp => kvp).ToList();
-        var updates = firstLookup.Where(kvp => secondLookup.Contains(kvp.Key)).SelectMany(kvp => kvp).ToList();
+        var adds = new List<T1>();
+        var removes = new List<T2>();
+        var updates = new List<T1>();
+
+        foreach (var kvp in firstLookup)
+        {
+            if (secondLookup.Contains(kvp.Key))
+            {
+                updates.AddRange(kvp);
+            }
+            else
+            {
+                adds.AddRange(kvp);
+            }
+        }
+
+        foreach (var kvp in secondLookup.Where(kvp => !firstLookup.Contains(kvp.Key)))
+        {
+            removes.AddRange(kvp);
+        }
+
         return (adds, removes, updates);
     }
 
@@ -1325,16 +1353,35 @@ public static class IEnumerableExtensions
     /// <param name="condition">对比因素条件</param>
     /// <returns></returns>
     [Obsolete("大数据集时性能不佳,请考虑使用其他重载")]
-    public static (List<T1> adds, List<T2> remove, List<(T1 first, T2 second)> updates) CompareChangesPlus<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> condition)
+    public static (List<T1> adds, List<T2> remove, List<(T1 first, T2 second)> updates) CompareChangesPlus<T1, T2>(
+        this IEnumerable<T1> first,
+        IEnumerable<T2> second,
+        Func<T1, T2, bool> condition)
     {
-        first ??= new List<T1>();
-        second ??= new List<T2>();
-        var firstSource = first as ICollection<T1> ?? first.ToList();
-        var secondSource = second as ICollection<T2> ?? second.ToList();
-        var add = firstSource.ExceptBy(secondSource, condition).ToList();
-        var remove = secondSource.ExceptBy(firstSource, (s, f) => condition(f, s)).ToList();
-        var updates = firstSource.IntersectBy(secondSource, condition).Select(t1 => (t1, secondSource.FirstOrDefault(t2 => condition(t1, t2)))).ToList();
-        return (add, remove, updates);
+        if (first == null) throw new ArgumentNullException(nameof(first));
+        if (second == null) throw new ArgumentNullException(nameof(second));
+        if (condition == null) throw new ArgumentNullException(nameof(condition));
+
+        var adds = new List<T1>();
+        var removes = new List<T2>();
+        var updates = new List<(T1 first, T2 second)>();
+        var secondSet = new HashSet<T2>(second);
+        foreach (var item in first)
+        {
+            var match = secondSet.FirstOrDefault(s => condition(item, s));
+            if (match != null)
+            {
+                updates.Add((item, match));
+                secondSet.Remove(match);
+            }
+            else
+            {
+                adds.Add(item);
+            }
+        }
+
+        removes.AddRange(secondSet);
+        return (adds, removes, updates);
     }
 
     /// <summary>

+ 16 - 4
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IEnumerableTest.cs

@@ -65,7 +65,11 @@ public class IEnumerableTest
             new MyClass3()
             {
                 Id = 1,
-            }
+            },
+            new MyClass3()
+            {
+                Id = 2,
+            },
         };
         var list2 = new List<MyClass3>()
         {
@@ -77,6 +81,14 @@ public class IEnumerableTest
             {
                 Id = 2,
             },
+            new MyClass3()
+            {
+                Id = 3
+            },
+            new MyClass3()
+            {
+                Id = 4
+            },
         };
 
         // act
@@ -91,9 +103,9 @@ public class IEnumerableTest
 
         // assert
         Assert.True(new[] { adds, adds1, adds2, adds3, adds4, adds5, adds6, adds7 }.All(x => x.Count == 1));
-        Assert.True(new[] { remove, remove1, remove2, remove3, remove4, remove5, remove6, remove7 }.All(x => x.Count == 1));
-        Assert.True(new[] { updates, updates1, updates2, updates3 }.All(x => x.Count == 1));
-        Assert.True(new[] { updates4, updates5, updates6, updates7 }.All(x => x.Count == 1));
+        Assert.True(new[] { remove, remove1, remove2, remove3, remove4, remove5, remove6, remove7 }.All(x => x.Count == 2));
+        Assert.True(new[] { updates, updates1, updates2, updates3 }.All(x => x.Count == 2));
+        Assert.True(new[] { updates4, updates5, updates6, updates7 }.All(x => x.Count == 2));
     }
 
     [Fact]