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 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_OR_GREATER #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 ICollection @this, IEnumerable 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)); } /// /// 异步Select /// /// /// /// /// /// 最大并行数 /// public static async Task> SelectAsync(this IEnumerable source, Func> selector, int maxParallelCount) { var results = new List(); var tasks = new List>(); foreach (var item in source) { var task = selector(item); tasks.Add(task); if (tasks.Count >= maxParallelCount) { results.AddRange(await Task.WhenAll(tasks)); tasks.Clear(); } } results.AddRange(await Task.WhenAll(tasks)); return results; } /// /// 异步Select /// /// /// /// /// /// 最大并行数 /// public static async Task> SelectAsync(this IEnumerable source, Func> selector, int maxParallelCount) { var results = new List(); var tasks = new List>(); int index = 0; foreach (var item in source) { var task = selector(item, index++); tasks.Add(task); if (tasks.Count >= maxParallelCount) { results.AddRange(await Task.WhenAll(tasks)); tasks.Clear(); } } results.AddRange(await Task.WhenAll(tasks)); return results; } /// /// 异步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<(T1 first, T2 second)> updates) CompareChangesPlus(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 updates = first.IntersectBy(second, condition).Select(t1 => (t1, second.FirstOrDefault(t2 => condition(t1, t2)))).ToList(); return (add, remove, updates); } /// /// 将集合声明为非null集合 /// /// /// /// public static List AsNotNull(this List list) { return list ?? new List(); } /// /// 将集合声明为非null集合 /// /// /// /// public static IEnumerable AsNotNull(this IEnumerable list) { return list ?? new List(); } /// /// 满足条件时执行筛选条件 /// /// /// /// /// /// public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func where) { return condition ? source.Where(where) : source; } /// /// 满足条件时执行筛选条件 /// /// /// /// /// /// public static IEnumerable WhereIf(this IEnumerable source, Func condition, Func where) { return condition() ? source.Where(where) : source; } /// /// 满足条件时执行筛选条件 /// /// /// /// /// /// public static IQueryable WhereIf(this IQueryable source, bool condition, Expression> where) { return condition ? source.Where(where) : source; } /// /// 满足条件时执行筛选条件 /// /// /// /// /// /// public static IQueryable WhereIf(this IQueryable source, Func condition, Expression> where) { return condition() ? source.Where(where) : source; } /// /// 改变元素的索引位置 /// /// /// 集合 /// 元素 /// 索引值 /// public static IList ChangeIndex(this IList list, T item, int index) { if (item is null) { throw new ArgumentNullException(nameof(item)); } ChangeIndexInternal(list, item, index); return list; } /// /// 改变元素的索引位置 /// /// /// 集合 /// 元素定位条件 /// 索引值 public static IList ChangeIndex(this IList list, Func condition, int index) { var item = list.FirstOrDefault(condition); if (item != null) { ChangeIndexInternal(list, item, index); } return list; } private static void ChangeIndexInternal(IList list, T item, int index) { index = Math.Max(0, index); index = Math.Min(list.Count - 1, index); list.Remove(item); list.Insert(index, item); } } }