namespace MeloongCore.Extensions; public static class EnumerableExtensions { #region 查找 /// /// 按优先级依次查找,返回首个有匹配结果的条件所匹配的第一个元素。 /// 所有条件均无匹配时返回 default。每尝试一个条件都会重新枚举序列。 /// public static TSource? FirstOrDefault(this IEnumerable source, params Func[] predicates) { foreach (var predicate in predicates) { foreach (var item in source) { if (predicate(item)) return item; } } return default; } #endregion #region 去重 /// /// 通过比较器进行去重。 /// 该方法的效率较低,建议仅在小型列表上使用,或换用 DistinctBy。 /// public static IEnumerable Distinct(this IList list, Func comparer) { var seen = new List(); foreach (var item in list) { if (!seen.Any(s => comparer(s, item))) { seen.Add(item); yield return item; } } } /// /// 按对象的指定值去重。 /// public static IEnumerable DistinctBy(this IEnumerable source, Func selector) { var seen = new HashSet(); foreach (var element in source) { if (seen.Add(selector(element))) yield return element; } } /// /// 按对象的指定值去重。 /// public static ParallelQuery DistinctBy(this ParallelQuery source, Func selector) { var seen = new ConcurrentDictionary(); return source.Where(element => seen.TryAdd(selector(element), true)); } #endregion #region 最大 / 最小 /// /// 选择所有最大值对应的对象。 /// 若没有元素则返回空列表。 /// public static List MaxByAll(this IEnumerable source, Func selector) where TKey : IComparable { var results = new List(); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return results; TSource maxItem = enumerator.Current; TKey maxValue = selector(maxItem); results.Add(maxItem); while (enumerator.MoveNext()) { TSource currentItem = enumerator.Current; TKey currentValue = selector(currentItem); int comparisonResult = currentValue.CompareTo(maxValue); if (comparisonResult > 0) { maxValue = currentValue; results.Clear(); results.Add(currentItem); } else if (comparisonResult == 0) { results.Add(currentItem); } } return results; } /// /// 选择所有最大值对应的对象。 /// 若没有元素则返回空列表。 /// public static List MaxByAll(this IEnumerable source, Func selector, IComparer comparer) { var results = new List(); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return results; TSource maxItem = enumerator.Current; TKey maxValue = selector(maxItem); results.Add(maxItem); while (enumerator.MoveNext()) { TSource currentItem = enumerator.Current; TKey currentValue = selector(currentItem); int comparisonResult = comparer.Compare(currentValue, maxValue); if (comparisonResult > 0) { maxValue = currentValue; results.Clear(); results.Add(currentItem); } else if (comparisonResult == 0) { results.Add(currentItem); } } return results; } /// /// 选择所有最小值对应的对象。 /// 若没有元素则返回空列表。 /// public static List MinByAll(this IEnumerable source, Func selector) where TKey : IComparable { var results = new List(); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return results; TSource minItem = enumerator.Current; TKey minValue = selector(minItem); results.Add(minItem); while (enumerator.MoveNext()) { TSource currentItem = enumerator.Current; TKey currentValue = selector(currentItem); int comparisonResult = currentValue.CompareTo(minValue); if (comparisonResult < 0) { minValue = currentValue; results.Clear(); results.Add(currentItem); } else if (comparisonResult == 0) { results.Add(currentItem); } } return results; } /// /// 选择所有最小值对应的对象。 /// 若没有元素则返回空列表。 /// public static List MinByAll(this IEnumerable source, Func selector, IComparer comparer) { var results = new List(); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return results; TSource minItem = enumerator.Current; TKey minValue = selector(minItem); results.Add(minItem); while (enumerator.MoveNext()) { TSource currentItem = enumerator.Current; TKey currentValue = selector(currentItem); int comparisonResult = comparer.Compare(currentValue, minValue); if (comparisonResult < 0) { minValue = currentValue; results.Clear(); results.Add(currentItem); } else if (comparisonResult == 0) { results.Add(currentItem); } } return results; } /// /// 选择最大值对应的对象。 /// 若没有元素则返回 null。 /// public static TSource? MaxBy(this IEnumerable source, Func selector) where TKey : IComparable { using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return default; TSource maxItem = enumerator.Current; TKey maxValue = selector(maxItem); while (enumerator.MoveNext()) { TKey value = selector(enumerator.Current); if (value.CompareTo(maxValue) <= 0) continue; maxItem = enumerator.Current; maxValue = value; } return maxItem; } /// /// 选择最大值对应的对象。 /// 若没有元素则返回 null。 /// public static TSource? MaxBy(this IEnumerable source, Func selector, IComparer comparer) { using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return default; TSource maxItem = enumerator.Current; TKey maxValue = selector(maxItem); while (enumerator.MoveNext()) { TKey value = selector(enumerator.Current); if (comparer.Compare(value, maxValue) <= 0) continue; maxItem = enumerator.Current; maxValue = value; } return maxItem; } /// /// 选择最小值对应的对象。 /// 若没有元素则返回 null。 /// public static TSource? MinBy(this IEnumerable source, Func selector) where TKey : IComparable { using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) { return default; } TSource minItem = enumerator.Current; TKey minValue = selector(minItem); while (enumerator.MoveNext()) { TKey value = selector(enumerator.Current); if (value.CompareTo(minValue) >= 0) { continue; } minItem = enumerator.Current; minValue = value; } return minItem; } /// /// 选择最小值对应的对象。 /// 若没有元素则返回 null。 /// public static TSource? MinBy(this IEnumerable source, Func selector, IComparer comparer) { using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) { return default; } TSource minItem = enumerator.Current; TKey minValue = selector(minItem); while (enumerator.MoveNext()) { TKey value = selector(enumerator.Current); if (comparer.Compare(value, minValue) >= 0) { continue; } minItem = enumerator.Current; minValue = value; } return minItem; } #endregion #region 排序 /// /// 使用默认比较器对集合进行排序。顺序为从小到大。 /// public static IEnumerable Ordered(this IEnumerable source) where T : IComparable => source.OrderBy(x => x); /// /// 使用默认比较器对集合进行排序。顺序为从大到小。 /// public static IEnumerable OrderedDescending(this IEnumerable source) where T : IComparable => source.OrderByDescending(x => x); #endregion #region Join /// /// 用指定的分割符将集合连接为字符串。 /// public static string Join(this IEnumerable list, char split) { var builder = new StringBuilder(); bool isFirst = true; foreach (var element in list) { if (isFirst) isFirst = false; else builder.Append(split); if (element is not null) builder.Append(element.ToString()); } return builder.ToString(); } /// /// 用指定的分割符将集合连接为字符串。 /// public static string Join(this IEnumerable list, string split) { if (split.IsSingle()) return list.Join(split[0]); var builder = new StringBuilder(); bool isFirst = true; foreach (var element in list) { if (isFirst) isFirst = false; else builder.Append(split); if (element is not null) builder.Append(element); } return builder.ToString(); } #endregion #region Dictionary /// /// 从 中移除具有指定键的元素。 /// 返回是否确实移除了元素。 /// public static bool Remove(this ConcurrentDictionary dict, TKey key) => dict.TryRemove(key, out _); /// /// 将二维数组转换为字典。 /// public static Dictionary ToDictionary(this T[,] arr) where T : notnull { Dictionary result = []; if (arr.Length == 0) return result; if (arr.GetLength(1) != 2) throw new ArgumentException("数组必须为两列,第一列为 Key,第二列为 Value。"); for (int i = 0; i < arr.GetLength(0); i++) result[arr[i, 0]] = arr[i, 1]; return result; } /// /// 尝试从字典中获取某项,如果该项不存在,则返回默认值。 /// public static TValue? GetOrDefault(this IDictionary dict, TKey key, TValue? defaultValue = default) => dict.TryGetValue(key, out var result) ? result : defaultValue; /// /// 将值添加到字典的对应项的列表中。 /// public static void AddIntoValueCollection(this IDictionary> dict, TKey key, TValue value) { if (dict.TryGetValue(key, out var collection)) { collection.Add(value); } else { dict.Add(key, [value]); } } #endregion /// /// 将 枚举值解包为它包含的单一 Flag 成员的集合。 /// 建议仅对没有极端值的正数枚举使用。 /// /// /// 会跳过:0、非 2 的幂的值、不在枚举值中的数、组合值、对应同一个枚举项的多个别名。 /// public static IEnumerable Flags(this T value) where T : struct, Enum => EnumUtils.GetAllFlags().Where(element => value.HasFlag(element)); /// /// 从集合中移除满足条件的元素。 /// public static void RemoveIf(this ICollection source, Func predicate) { var itemsToRemove = source.Where(predicate).ToList(); foreach (var item in itemsToRemove) source.Remove(item); } /// /// 从集合中移除另一个集合中的所有元素。 /// public static void RemoveAll(this ICollection source, IEnumerable itemsToRemove) { foreach (var item in itemsToRemove) source.Remove(item); } /// /// 仅保留满足条件的元素,移除不满足条件的元素。 /// public static void KeepIf(this ICollection source, Func predicate) { var itemsToRemove = source.Where(item => !predicate(item)).ToList(); foreach (var item in itemsToRemove) source.Remove(item); } /// /// 从集合中移除指定元素。 /// public static IEnumerable Except(this IEnumerable source, T element) => source.Except([element]); /// /// 判断集合是否有且仅有一个元素。 /// 在输入 null 时返回 false。 /// public static bool IsSingle(this IEnumerable? source) { if (source is null) return false; if (source is IList list) return list.Count == 1; using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return false; if (enumerator.MoveNext()) return false; return true; } /// /// 对集合的每个元素执行指定操作。 /// public static IEnumerable ForAll(this IEnumerable source, Action action) { foreach (T item in source) action(item); return source; } }