namespace MeloongCore.Extensions; public static class EnumerableExtensions { #region Distinct /// /// 通过比较器进行去重。 /// 该方法的效率较低,建议仅在小型列表上使用,或换用 DistinctBy。 /// public static IEnumerable Distinct(this IList list, Func comparer) { for (int i = 0; i < list.Count; i++) { if (!list.Skip(i + 1).Any(item => comparer(list[i], item))) yield return list[i]; } } /// /// 按对象的指定值去重。 /// 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; } } #endregion #region MinBy / MaxBy /// /// 选择所有最大值对应的对象。 /// 若没有元素则返回空列表。 /// public static List MaxByAll(this IEnumerable source, Func selector) where C : IComparable { var results = new List(); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return results; T maxItem = enumerator.Current; C maxValue = selector(maxItem); results.Add(maxItem); while (enumerator.MoveNext()) { T currentItem = enumerator.Current; C 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 MinByAll(this IEnumerable source, Func selector) where C : IComparable { var results = new List(); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return results; T minItem = enumerator.Current; C minValue = selector(minItem); results.Add(minItem); while (enumerator.MoveNext()) { T currentItem = enumerator.Current; C 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; } /// /// 选择最大值对应的对象。 /// 若没有元素则返回 null。 /// public static T? MaxBy(this IEnumerable source, Func selector) where C : IComparable { using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) return default; T maxItem = enumerator.Current; C maxValue = selector(maxItem); while (enumerator.MoveNext()) { C value = selector(enumerator.Current); if (value.CompareTo(maxValue) <= 0) continue; maxItem = enumerator.Current; maxValue = value; } return maxItem; } /// /// 选择最小值对应的对象。 /// 若没有元素则返回 null。 /// public static T? MinBy(this IEnumerable List, Func Selector) where C : IComparable { using var enumerator = List.GetEnumerator(); if (!enumerator.MoveNext()) { return default; } T minItem = enumerator.Current; C minValue = Selector(minItem); while (enumerator.MoveNext()) { C value = Selector(enumerator.Current); if (value.CompareTo(minValue) >= 0) { continue; } minItem = enumerator.Current; minValue = value; } return minItem; } #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.ToString()); } return builder.ToString(); } #endregion /// /// 判断集合是否有且仅有一个元素。 /// 在输入 null 时返回 false。 /// public static bool IsSingle([AllowNull] 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 ForEach(this IEnumerable source, Action action) { foreach (T item in source) action(item); return source; } }