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;
}
}