< Summary

Information
Class: MoreStructures.Utilities.EnumerableExtensions
Assembly: MoreStructures
File(s): /home/runner/work/MoreStructures/MoreStructures/MoreStructures/Utilities/EnumerableExtensions.cs
Line coverage
100%
Covered lines: 104
Uncovered lines: 0
Coverable lines: 104
Total lines: 255
Line coverage: 100%
Branch coverage
100%
Covered branches: 90
Total branches: 90
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
CountO1(...)100%8100%
ElementAtO1(...)100%18100%
ElementAtO1(...)100%18100%
ElementAtO1OrDefault(...)100%20100%
EnumerateExactlyFirst(...)100%2100%
EnumerateAtMostFirst(...)100%6100%
ToEnumerable()100%2100%
SkipO1()100%16100%

File(s)

/home/runner/work/MoreStructures/MoreStructures/MoreStructures/Utilities/EnumerableExtensions.cs

#LineLine coverage
 1using System.Collections;
 2
 3namespace MoreStructures.Utilities;
 4
 5/// <summary>
 6/// Extension methods for all <see cref="IEnumerable{T}"/> concretions.
 7/// </summary>
 8public static class EnumerableExtensions
 9{
 10    /// <summary>
 11    /// Optimized version of <see cref="Enumerable.Count{TSource}(IEnumerable{TSource})"/>, which runs in constant time
 12    /// on <paramref name="source"/> of type <see cref="string"/>, <see cref="IDictionary"/>, <see cref="IList{T}"/>
 13    /// and <see cref="IList"/>, and calls <see cref="Enumerable.Count{TSource}(IEnumerable{TSource})"/> for any
 14    /// <paramref name="source"/> which cannot be assigned to either of these types.
 15    /// </summary>
 16    /// <typeparam name="TSource">
 17    ///     <inheritdoc cref="Enumerable.Count{TSource}(IEnumerable{TSource})" path="/typeparam[@name='TSource']"/>
 18    /// </typeparam>
 19    /// <param name="source">
 20    ///     <inheritdoc cref="Enumerable.Count{TSource}(IEnumerable{TSource})" path="/param[@name='source']"/>
 21    /// </param>
 22    /// <returns>
 23    ///     <inheritdoc cref="Enumerable.Count{TSource}(IEnumerable{TSource})" path="/returns"/>
 24    /// </returns>
 25    public static int CountO1<TSource>(this IEnumerable<TSource> source)
 1268026    {
 1268027        if (source is string str)
 50728            return str.Length;
 1217329        if (source is IDictionary nonGenericDict)
 330            return nonGenericDict.Count;
 1217031        if (source is IList<TSource> genericList)
 117432            return genericList.Count;
 1099633        if (source is IList nonGenericList)
 134            return nonGenericList.Count;
 1099535        return source.Count();
 1268036    }
 37
 38    /// <inheritdoc cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, int)" path="//*[not(self::summary)]"/>
 39    /// <summary>
 40    /// Optimized version of <see cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, int)"/>, which runs in
 41    /// constant time on <paramref name="source"/> of type <see cref="string"/>, <see cref="IList{T}"/>
 42    /// and <see cref="IList"/>, and calls <see cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, int)"/>
 43    /// for any <paramref name="source"/> which cannot be assigned to either of these types.
 44    /// </summary>
 45    public static TSource ElementAtO1<TSource>(this IEnumerable<TSource> source, int index)
 549446    {
 549447        if (source is string str)
 161448        {
 161449            if (index < 0 || index >= str.Length)
 250                throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 161251            return (TSource)(str[index] as object);
 52        }
 53
 388054        if (source is IList<TSource> genericList)
 270255        {
 270256            if (index < 0 || index >= genericList.Count)
 657                throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 269658            return genericList[index];
 59        }
 60
 117861        if (source is IList nonGenericList)
 462        {
 463            if (index < 0 || index >= nonGenericList.Count)
 264                throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 265            return (TSource)nonGenericList[index]!;
 66        }
 67
 117468        return source.ElementAt(index);
 548469    }
 70
 71    /// <inheritdoc cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, int)" path="//*[not(self::summary)]"/>
 72    /// <summary>
 73    /// Optimized version of <see cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, Index)"/>, which runs in
 74    /// constant time on <paramref name="source"/> of type <see cref="string"/>, <see cref="IList{T}"/>
 75    /// and <see cref="IList"/>, and calls <see cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, int)"/>
 76    /// for any <paramref name="source"/> which cannot be assigned to either of these types.
 77    /// </summary>
 78    public static TSource ElementAtO1<TSource>(this IEnumerable<TSource> source, Index index)
 18221579    {
 18221580        if (source is string str)
 1281        {
 1282            var indexValue = index.GetOffset(str.Length);
 1283            if (indexValue < 0 || indexValue >= str.Length)
 1084                throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 285            return (TSource)(str[index] as object);
 86        }
 87
 18220388        if (source is IList<TSource> genericList)
 3089        {
 3090            var indexValue = index.GetOffset(genericList.Count);
 3091            if (indexValue < 0 || indexValue >= genericList.Count)
 2492                throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 693            return genericList[index];
 94        }
 95
 18217396        if (source is IList nonGenericList)
 697        {
 698            var indexValue = index.GetOffset(nonGenericList.Count);
 699            if (indexValue < 0 || indexValue >= nonGenericList.Count)
 4100                throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 2101            return (TSource)nonGenericList[index]!;
 102        }
 103
 182167104        return source.ElementAt(index);
 182177105    }
 106
 107    /// <summary>
 108    /// Optimized version of <see cref="Enumerable.ElementAtOrDefault{TSource}(IEnumerable{TSource}, int)"/>, which
 109    /// runs in constant time on <paramref name="source"/> of type <see cref="string"/>, <see cref="IList{T}"/>
 110    /// and <see cref="IList"/>, and calls
 111    /// <see cref="Enumerable.ElementAtOrDefault{TSource}(IEnumerable{TSource}, int)"/>
 112    /// for any <paramref name="source"/> which cannot be assigned to either of these types.
 113    /// </summary>
 114    /// <typeparam name="TSource">
 115    ///     <inheritdoc cref="Enumerable.ElementAtOrDefault{TSource}(IEnumerable{TSource}, int)"
 116    ///         path="/typeparam[@name='TSource']"/>
 117    /// </typeparam>
 118    /// <param name="source">
 119    ///     <inheritdoc cref="Enumerable.ElementAtOrDefault{TSource}(IEnumerable{TSource}, int)"
 120    ///         path="/param[@name='source']"/>
 121    /// </param>
 122    /// <param name="index">
 123    ///     <inheritdoc cref="Enumerable.ElementAtOrDefault{TSource}(IEnumerable{TSource}, int)"
 124    ///         path="/param[@name='index']"/>
 125    /// </param>
 126    /// <returns>
 127    ///     <inheritdoc cref="Enumerable.ElementAtOrDefault{TSource}(IEnumerable{TSource}, int)"
 128    ///         path="/returns"/>
 129    /// </returns>
 130    public static TSource? ElementAtO1OrDefault<TSource>(this IEnumerable<TSource> source, int index)
 302131    {
 302132        if (index < 0)
 5133            throw new ArgumentOutOfRangeException($"Invalid {nameof(index)}: {index}");
 134
 297135        if (source is string str)
 26136            return index >= 0 && index < str.Length ? (TSource)(str[index] as object) : default;
 271137        if (source is IList<TSource> genericList)
 48138            return index >= 0 && index < genericList.Count ? genericList[index] : default;
 223139        if (source is IList nonGenericList)
 3140            return index >= 0 && index < nonGenericList.Count ? (TSource)nonGenericList[index]! : default;
 220141        return source.ElementAtOrDefault(index);
 297142    }
 143
 144    /// <summary>
 145    /// Eagerly enumerates the first <paramref name="count"/> items of <paramref name="source"/>, returning an
 146    /// <see cref="IList{T}"/> of them and the reminder, as a lazily evaluated <see cref="IEnumerable{T}"/>.
 147    /// </summary>
 148    /// <typeparam name="TSource">The type of elements of <paramref name="source"/>.</typeparam>
 149    /// <param name="source">The <see cref="IEnumerable{T}"/> to split into two pieces.</param>
 150    /// <param name="count">The number of items of <paramref name="source"/> to eagerly evaluate and return.</param>
 151    /// <returns>
 152    /// A couple of an <see cref="IList{T}"/> instance and an <see cref="IEnumerable{T}"/> instance.
 153    /// </returns>
 154    public static (IList<TSource> firstNItems, IEnumerable<TSource> reminder) EnumerateExactlyFirst<TSource>(
 155        this IEnumerable<TSource> source, int count)
 121156    {
 121157        var (first, reminder) = source.EnumerateAtMostFirst(count);
 158
 119159        if (first.Count < count)
 4160            throw new ArgumentException($"Sequence doesn't contain {count} elements.", nameof(source));
 161
 115162        return (first, reminder);
 115163    }
 164
 165    /// <summary>
 166    /// Eagerly enumerates the first <paramref name="count"/> items of <paramref name="source"/>, or less if there
 167    /// aren't enough, returning an <see cref="IList{T}"/> of them and the reminder, as a lazily evaluated
 168    /// <see cref="IEnumerable{T}"/>.
 169    /// </summary>
 170    /// <typeparam name="TSource">The type of elements of <paramref name="source"/>.</typeparam>
 171    /// <param name="source">The <see cref="IEnumerable{T}"/> to split into two pieces.</param>
 172    /// <param name="count">
 173    /// The number of items of <paramref name="source"/> to eagerly evaluate and return (at most).
 174    /// </param>
 175    /// <returns>
 176    /// A couple of an <see cref="IList{T}"/> instance and an <see cref="IEnumerable{T}"/> instance.
 177    /// </returns>
 178    public static (IList<TSource> firstNItems, IEnumerable<TSource> reminder) EnumerateAtMostFirst<TSource>(
 179        this IEnumerable<TSource> source, int count)
 164180    {
 164181        if (count < 0)
 2182            throw new ArgumentOutOfRangeException($"Invalid {nameof(count)}: {count}");
 183
 162184        var enumerator = source.GetEnumerator();
 185
 162186        var first = new List<TSource> { };
 666187        for (var i = 0; i < count; i++)
 185188        {
 185189            if (!enumerator.MoveNext())
 14190                break;
 191
 171192            first.Add(enumerator.Current);
 171193        }
 194
 162195        return (first, ToEnumerable(enumerator));
 162196    }
 197
 198    private static IEnumerable<TSource> ToEnumerable<TSource>(IEnumerator<TSource> enumerator)
 109199    {
 668200        while (enumerator.MoveNext())
 559201            yield return enumerator.Current;
 109202    }
 203
 204    /// <summary>
 205    /// Optimized version of <see cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"/>, which
 206    /// runs in constant time on <paramref name="source"/> of type <see cref="string"/>, <see cref="IList{T}"/>
 207    /// and <see cref="IList"/>, and calls
 208    /// <see cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"/>
 209    /// for any <paramref name="source"/> which cannot be assigned to either of these types.
 210    /// </summary>
 211    /// <typeparam name="TSource">
 212    ///     <inheritdoc cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"
 213    ///         path="/typeparam[@name='TSource']"/>
 214    /// </typeparam>
 215    /// <param name="source">
 216    ///     <inheritdoc cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"
 217    ///         path="/param[@name='source']"/>
 218    /// </param>
 219    /// <param name="count">
 220    ///     <inheritdoc cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"
 221    ///         path="/param[@name='count']"/>
 222    /// </param>
 223    /// <returns>
 224    ///     <inheritdoc cref="Enumerable.Skip{TSource}(IEnumerable{TSource}, int)"
 225    ///         path="/returns"/>
 226    /// </returns>
 227    public static IEnumerable<TSource> SkipO1<TSource>(this IEnumerable<TSource> source, int count)
 637228    {
 637229        count = count >= 0 ? count : 0;
 230
 637231        if (source is string str)
 98232        {
 1374233            while (count < str.Length)
 1367234                yield return (TSource)(str[count++] as object);
 7235            yield break;
 236        }
 237
 539238        if (source is IList<TSource> genericList)
 430239        {
 2673240            while (count < genericList.Count)
 2666241                yield return genericList[count++];
 7242            yield break;
 243        }
 244
 109245        if (source is IList nonGenericList)
 7246        {
 18247            while (count < nonGenericList.Count)
 11248                yield return (TSource)nonGenericList[count++]!;
 7249            yield break;
 250        }
 251
 1961252        foreach (var item in source.Skip(count))
 875253            yield return item;
 7254    }
 255}