#nullable enable using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace Kavita.Common.Extensions; public static class EnumerableExtensions { private static readonly Regex Regex = new Regex(@"\d+", RegexOptions.Compiled, TimeSpan.FromMilliseconds(500)); /// /// A natural sort implementation /// /// IEnumerable to process /// Function that produces a string. Does not support null values /// Defaults to CurrentCulture /// /// Sorted Enumerable public static IEnumerable OrderByNatural(this IEnumerable items, Func selector, StringComparer? stringComparer = null) { var list = items.ToList(); var maxDigits = list .SelectMany(i => Regex.Matches(selector(i)) .Select(digitChunk => (int?)digitChunk.Value.Length)) .Max() ?? 0; return list.OrderBy(i => Regex.Replace(selector(i), match => match.Value.PadLeft(maxDigits, '0')), stringComparer ?? StringComparer.CurrentCulture); } /// /// extension(IList source) { /// /// Safety net around Max, returning the default value if the source contains no elements /// /// /// /// /// public TResult? MaxOrDefault(Func selector, TResult? defaultValue) { return source.Count == 0 ? defaultValue : source.Max(selector); } /// /// Safety wrapper around Min, returning the default value if the source has no elements /// /// /// /// /// public TResult? MinOrDefault(Func selector, TResult? defaultValue) { return source.Count == 0 ? defaultValue : source.Min(selector); } } public static IEnumerable WhereNotNull(this IEnumerable source) where TSource : class { return source.Where(item => item != null)!; } }