#nullable enable using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; namespace Masuit.Tools { public static partial class IEnumerableExtensions { /// /// 按字段属性判等取交集 /// /// /// /// /// /// /// public static IEnumerable IntersectBy(this IEnumerable first, IEnumerable second, Func condition) { return first.Where(f => second.Any(s => condition(f, s))); } /// /// 按字段属性判等取差集 /// /// /// /// /// /// /// public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func condition) { return first.Where(f => !second.Any(s => condition(f, s))); } #if NET6_0 #else /// /// 按字段去重 /// /// /// /// /// /// public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) { var hash = new HashSet(); return source.Where(p => hash.Add(keySelector(p))); } /// /// 按字段属性判等取交集 /// /// /// /// /// /// /// public static IEnumerable IntersectBy(this IEnumerable first, IEnumerable second, Func keySelector) { return first.IntersectBy(second, keySelector, null); } /// /// 按字段属性判等取交集 /// /// /// /// /// /// /// public static IEnumerable IntersectBy(this IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return IntersectByIterator(first, second, keySelector, comparer); } private static IEnumerable IntersectByIterator(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer comparer) { var set = new HashSet(second, comparer); foreach (var source in first) { if (set.Remove(keySelector(source))) yield return source; } } /// /// 按字段属性判等取差集 /// /// /// /// /// /// /// public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector) { return first.ExceptBy(second, keySelector, null); } /// /// 按字段属性判等取差集 /// /// /// /// /// /// /// /// /// public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return ExceptByIterator(first, second, keySelector, comparer); } private static IEnumerable ExceptByIterator(IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer comparer) { var set = new HashSet(second, comparer); foreach (var source in first) { if (set.Add(keySelector(source))) yield return source; } } #endif /// /// 添加多个元素 /// /// /// /// public static void AddRange(this ICollection @this, params T[] values) { foreach (var obj in values) { @this.Add(obj); } } /// /// 添加多个元素 /// /// /// /// public static void AddRange(this ConcurrentBag @this, params T[] values) { foreach (var obj in values) { @this.Add(obj); } } /// /// 添加多个元素 /// /// /// /// public static void AddRange(this ConcurrentQueue @this, params T[] values) { foreach (var obj in values) { @this.Enqueue(obj); } } /// /// 添加符合条件的多个元素 /// /// /// /// /// public static void AddRangeIf(this ICollection @this, Func predicate, params T[] values) { foreach (var obj in values) { if (predicate(obj)) { @this.Add(obj); } } } /// /// 添加符合条件的多个元素 /// /// /// /// /// public static void AddRangeIf(this ConcurrentBag @this, Func predicate, params T[] values) { foreach (var obj in values) { if (predicate(obj)) { @this.Add(obj); } } } /// /// 添加符合条件的多个元素 /// /// /// /// /// public static void AddRangeIf(this ConcurrentQueue @this, Func predicate, params T[] values) { foreach (var obj in values) { if (predicate(obj)) { @this.Enqueue(obj); } } } /// /// 添加不重复的元素 /// /// /// /// public static void AddRangeIfNotContains(this ICollection @this, params T[] values) { foreach (T obj in values) { if (!@this.Contains(obj)) { @this.Add(obj); } } } /// /// 移除符合条件的元素 /// /// /// /// public static void RemoveWhere(this ICollection @this, Func @where) { foreach (var obj in @this.Where(where).ToList()) { @this.Remove(obj); } } /// /// 在元素之后添加元素 /// /// /// /// 条件 /// 值 public static void InsertAfter(this IList list, Func condition, T value) { foreach (var item in list.Select((item, index) => new { item, index }).Where(p => condition(p.item)).OrderByDescending(p => p.index)) { if (item.index + 1 == list.Count) { list.Add(value); } else { list.Insert(item.index + 1, value); } } } /// /// 在元素之后添加元素 /// /// /// /// 索引位置 /// 值 public static void InsertAfter(this IList list, int index, T value) { foreach (var item in list.Select((v, i) => new { Value = v, Index = i }).Where(p => p.Index == index).OrderByDescending(p => p.Index)) { if (item.Index + 1 == list.Count) { list.Add(value); } else { list.Insert(item.Index + 1, value); } } } /// /// 转HashSet /// /// /// /// /// /// public static HashSet ToHashSet(this IEnumerable source, Func selector) { var set = new HashSet(); set.UnionWith(source.Select(selector)); return set; } /// /// 遍历IEnumerable /// /// /// 回调方法 /// public static void ForEach(this IEnumerable objs, Action action) { foreach (var o in objs) { action(o); } } /// /// 异步foreach /// /// /// /// 最大并行数 /// /// /// public static async Task ForeachAsync(this IEnumerable source, Func action, int maxParallelCount, CancellationToken cancellationToken = default) { if (Debugger.IsAttached) { foreach (var item in source) { await action(item); } return; } var list = new List(); foreach (var item in source) { if (cancellationToken.IsCancellationRequested) { return; } list.Add(action(item)); if (list.Count >= maxParallelCount) { await Task.WhenAll(list); list.Clear(); } } await Task.WhenAll(list); } /// /// 异步foreach /// /// /// /// /// public static Task ForeachAsync(this IEnumerable source, Func action, CancellationToken cancellationToken = default) { return ForeachAsync(source, action, source.Count(), cancellationToken); } /// /// 异步Select /// /// /// /// /// /// public static Task SelectAsync(this IEnumerable source, Func> selector) { return Task.WhenAll(source.Select(selector)); } /// /// 异步Select /// /// /// /// /// /// public static Task SelectAsync(this IEnumerable source, Func> selector) { return Task.WhenAll(source.Select(selector)); } /// /// 异步For /// /// /// /// /// 最大并行数 /// 取消口令 /// public static async Task ForAsync(this IEnumerable source, Func selector, int maxParallelCount, CancellationToken cancellationToken = default) { int index = 0; if (Debugger.IsAttached) { foreach (var item in source) { await selector(item, index); index++; } return; } var list = new List(); foreach (var item in source) { if (cancellationToken.IsCancellationRequested) { return; } list.Add(selector(item, index)); Interlocked.Add(ref index, 1); if (list.Count >= maxParallelCount) { await Task.WhenAll(list); list.Clear(); } } await Task.WhenAll(list); } /// /// 异步For /// /// /// /// /// 取消口令 /// public static Task ForAsync(this IEnumerable source, Func selector, CancellationToken cancellationToken = default) { return ForAsync(source, selector, source.Count(), cancellationToken); } /// /// 取最大值 /// /// /// /// /// /// public static TResult MaxOrDefault(this IQueryable source, Expression> selector) => source.Select(selector).DefaultIfEmpty().Max(); /// /// 取最大值 /// /// /// /// /// /// /// public static TResult MaxOrDefault(this IQueryable source, Expression> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max(); /// /// 取最大值 /// /// /// /// public static TSource MaxOrDefault(this IQueryable source) => source.DefaultIfEmpty().Max(); /// /// 取最大值 /// /// /// /// /// public static TSource MaxOrDefault(this IQueryable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max(); /// /// 取最大值 /// /// /// /// /// /// /// public static TResult MaxOrDefault(this IEnumerable source, Func selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max(); /// /// 取最大值 /// /// /// /// /// /// public static TResult MaxOrDefault(this IEnumerable source, Func selector) => source.Select(selector).DefaultIfEmpty().Max(); /// /// 取最大值 /// /// /// /// public static TSource MaxOrDefault(this IEnumerable source) => source.DefaultIfEmpty().Max(); /// /// 取最大值 /// /// /// /// /// public static TSource MaxOrDefault(this IEnumerable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max(); /// /// 取最小值 /// /// /// /// /// /// public static TResult MinOrDefault(this IQueryable source, Expression> selector) => source.Select(selector).DefaultIfEmpty().Min(); /// /// 取最小值 /// /// /// /// /// /// /// public static TResult MinOrDefault(this IQueryable source, Expression> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min(); /// /// 取最小值 /// /// /// /// public static TSource MinOrDefault(this IQueryable source) => source.DefaultIfEmpty().Min(); /// /// 取最小值 /// /// /// /// /// public static TSource MinOrDefault(this IQueryable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min(); /// /// 取最小值 /// /// /// /// /// /// public static TResult MinOrDefault(this IEnumerable source, Func selector) => source.Select(selector).DefaultIfEmpty().Min(); /// /// 取最小值 /// /// /// /// /// /// /// public static TResult MinOrDefault(this IEnumerable source, Func selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min(); /// /// 取最小值 /// /// /// /// public static TSource MinOrDefault(this IEnumerable source) => source.DefaultIfEmpty().Min(); /// /// 取最小值 /// /// /// /// /// public static TSource MinOrDefault(this IEnumerable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min(); /// /// 标准差 /// /// /// /// /// public static TResult StandardDeviation(this IEnumerable source, Func selector) where TResult : IConvertible { return StandardDeviation(source.Select(t => selector(t).ConvertTo())).ConvertTo(); } /// /// 标准差 /// /// /// /// public static T StandardDeviation(this IEnumerable source) where T : IConvertible { return StandardDeviation(source.Select(t => t.ConvertTo())).ConvertTo(); } /// /// 标准差 /// /// /// public static double StandardDeviation(this IEnumerable source) { double result = 0; int count = source.Count(); if (count > 1) { double avg = source.Average(); double sum = source.Sum(d => (d - avg) * (d - avg)); result = Math.Sqrt(sum / count); } return result; } /// /// 随机排序 /// /// /// /// public static IOrderedEnumerable OrderByRandom(this IEnumerable source) { return source.OrderBy(_ => Guid.NewGuid()); } /// /// 序列相等 /// /// /// /// /// /// public static bool SequenceEqual(this IEnumerable first, IEnumerable second, Func condition) { if (first is ICollection source1 && second is ICollection source2) { if (source1.Count != source2.Count) { return false; } if (source1 is IList list1 && source2 is IList list2) { int count = source1.Count; for (int index = 0; index < count; ++index) { if (!condition(list1[index], list2[index])) { return false; } } return true; } } using IEnumerator enumerator1 = first.GetEnumerator(); using IEnumerator enumerator2 = second.GetEnumerator(); while (enumerator1.MoveNext()) { if (!enumerator2.MoveNext() || !condition(enumerator1.Current, enumerator2.Current)) { return false; } } return !enumerator2.MoveNext(); } /// /// 序列相等 /// /// /// /// /// /// /// public static bool SequenceEqual(this IEnumerable first, IEnumerable second, Func condition) { if (first is ICollection source1 && second is ICollection source2) { if (source1.Count != source2.Count) { return false; } if (source1 is IList list1 && source2 is IList list2) { int count = source1.Count; for (int index = 0; index < count; ++index) { if (!condition(list1[index], list2[index])) { return false; } } return true; } } using IEnumerator enumerator1 = first.GetEnumerator(); using IEnumerator enumerator2 = second.GetEnumerator(); while (enumerator1.MoveNext()) { if (!enumerator2.MoveNext() || !condition(enumerator1.Current, enumerator2.Current)) { return false; } } return !enumerator2.MoveNext(); } /// /// 对比两个集合哪些是新增的、删除的、修改的 /// /// /// /// /// /// 对比因素条件 /// public static (List adds, List remove, List updates) CompareChanges(this IEnumerable first, IEnumerable second, Func condition) { var add = first.ExceptBy(second, condition).ToList(); var remove = second.ExceptBy(first, (s, f) => condition(f, s)).ToList(); var update = first.IntersectBy(second, condition).ToList(); return (add, remove, update); } /// /// 对比两个集合哪些是新增的、删除的、修改的 /// /// /// /// /// 对比因素条件 /// public static (List adds, List remove, List updates) CompareChanges(this IEnumerable first, IEnumerable second, Func condition) { var add = first.ExceptBy(second, condition).ToList(); var remove = second.ExceptBy(first, (s, f) => condition(f, s)).ToList(); var update = first.IntersectBy(second, condition).ToList(); return (add, remove, update); } } }