< Summary

Information
Class: MyNet.Collections.ListExtensions
Assembly: MyNet.Collections
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Collections/Extensions/ListExtensions.cs
Tag: 323_28699572109
Line coverage
92%
Covered lines: 58
Uncovered lines: 5
Coverable lines: 63
Total lines: 187
Line coverage: 92%
Branch coverage
82%
Covered branches: 33
Total branches: 40
Branch coverage: 82.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Swap(...)100%66100%
Sort(...)50%2262.5%
Sort(...)100%22100%
SortDescending(...)100%11100%
SortDescending(...)100%11100%
SortByDisplay(...)33.33%6677.77%
GetByIndex(...)75%44100%
UpdateFrom(...)94.44%1818100%
Replace(...)100%22100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Collections/Extensions/ListExtensions.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="ListExtensions.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections;
 9using System.Collections.Generic;
 10using System.Globalization;
 11using System.Linq;
 12using MyNet.Primitives;
 13
 14#pragma warning disable IDE0130 // Namespace does not match folder structure
 15namespace MyNet.Collections;
 16#pragma warning restore IDE0130 // Namespace does not match folder structure
 17
 18public static class ListExtensions
 19{
 20    extension(IList list)
 21    {
 22        /// <summary>
 23        /// Swaps the elements at the specified indices in the list. If either index is out of range or both indices are
 24        /// </summary>
 25        /// <param name="firstIndex">The index of the first element to swap.</param>
 26        /// <param name="secondIndex">The index of the second element to swap.</param>
 27        /// <exception cref="ArgumentNullException">Thrown if the list is null.</exception>
 28        public void Swap(int firstIndex, int secondIndex)
 29        {
 1230            ArgumentNullException.ThrowIfNull(list);
 31
 1232            if ((uint)firstIndex >= (uint)list.Count || (uint)secondIndex >= (uint)list.Count || firstIndex == secondInd
 933                return;
 34
 335            (list[firstIndex], list[secondIndex]) = (list[secondIndex], list[firstIndex]);
 336        }
 37    }
 38
 39    extension<T>(IList<T> list)
 40    {
 41        /// <summary>
 42        /// Sorts the collection in place using the specified key selector.
 43        /// </summary>
 44        public void Sort<TKey>(Func<T, TKey> keySelector)
 45        {
 346            ArgumentNullException.ThrowIfNull(list);
 347            ArgumentNullException.ThrowIfNull(keySelector);
 48
 349            if (list is List<T> concrete)
 50            {
 351                concrete.Sort((a, b) => Comparer<TKey>.Default.Compare(keySelector(a), keySelector(b)));
 352                return;
 53            }
 54
 055            var sorted = list.OrderBy(keySelector).ToArray();
 056            Replace(list, sorted);
 057        }
 58
 59        /// <summary>
 60        /// Sorts the collection in place using the specified key selector and comparer.
 61        /// </summary>
 62        public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
 63        {
 964            ArgumentNullException.ThrowIfNull(list);
 965            ArgumentNullException.ThrowIfNull(keySelector);
 966            ArgumentNullException.ThrowIfNull(comparer);
 67
 968            if (list is List<T> concrete)
 69            {
 370                concrete.Sort((a, b) => comparer.Compare(keySelector(a), keySelector(b)));
 371                return;
 72            }
 73
 674            var sorted = list.OrderBy(keySelector, comparer).ToArray();
 675            Replace(list, sorted);
 676        }
 77
 78        /// <summary>
 79        /// Sorts the collection in descending order using the specified key selector.
 80        /// </summary>
 381        public void SortDescending<TKey>(Func<T, TKey> keySelector) => list.Sort(keySelector, Comparer<TKey>.Default.Rev
 82
 83        /// <summary>
 84        /// Sorts the collection in descending order using the specified key selector.
 85        /// </summary>
 386        public void SortDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer) => list.Sort(keySelector, 
 87
 88        /// <summary>
 89        /// Sorts the collection in place using culture-aware, case-insensitive display text.
 90        /// </summary>
 91        public void SortByDisplay(
 92            Func<T, string> displaySelector,
 93            CultureInfo? culture = null,
 94            CompareOptions options = CompareOptions.IgnoreCase)
 95        {
 396            ArgumentNullException.ThrowIfNull(list);
 397            ArgumentNullException.ThrowIfNull(displaySelector);
 98
 399            var compareInfo = (culture ?? CultureInfo.CurrentCulture).CompareInfo;
 100
 3101            if (list is List<T> concrete)
 102            {
 3103                concrete.Sort((left, right) =>
 3104                    compareInfo.Compare(displaySelector(left), displaySelector(right), options));
 3105                return;
 106            }
 107
 0108            list.Sort(displaySelector, StringComparer.Create(culture ?? CultureInfo.CurrentCulture, options));
 0109        }
 110
 111        /// <summary>
 112        /// Gets the element at the specified index or returns a default value if the index is out of range.
 113        /// </summary>
 114        /// <param name="index">The index of the element to retrieve.</param>
 115        /// <param name="defaultValue">The default value to return if the index is out of range.</param>
 116        /// <returns>The element at the specified index or the default value if the index is out of range.</returns>
 117        /// <exception cref="ArgumentNullException">Thrown if the list is null.</exception>
 118        public T? GetByIndex(int index, T? defaultValue = default)
 119        {
 6120            ArgumentNullException.ThrowIfNull(list);
 6121            return index >= 0 && index < list.Count ? list[index] : defaultValue;
 122        }
 123
 124        public void UpdateFrom<TSource, TKey>(
 125            IEnumerable<TSource> source,
 126            Func<TSource, TKey> sourceKey,
 127            Func<T, TKey> destKey,
 128            Action<TSource> add,
 129            Action<T> remove,
 130            Action<T, TSource> update)
 131            where TKey : notnull
 132        {
 3133            ArgumentNullException.ThrowIfNull(list);
 3134            ArgumentNullException.ThrowIfNull(source);
 3135            ArgumentNullException.ThrowIfNull(sourceKey);
 3136            ArgumentNullException.ThrowIfNull(destKey);
 3137            ArgumentNullException.ThrowIfNull(add);
 3138            ArgumentNullException.ThrowIfNull(remove);
 3139            ArgumentNullException.ThrowIfNull(update);
 140
 3141            var sourceList = source as IList<TSource> ?? [.. source];
 142
 3143            var sourceMap = new Dictionary<TKey, TSource>(sourceList.Count);
 18144            foreach (var s in sourceList)
 6145                sourceMap[sourceKey(s)] = s;
 146
 3147            var destMap = new Dictionary<TKey, T>(list.Count);
 18148            foreach (var d in list)
 6149                destMap[destKey(d)] = d;
 150
 151            // REMOVE
 18152            foreach (var (key, dest) in destMap)
 153            {
 6154                if (!sourceMap.ContainsKey(key))
 3155                    remove(dest);
 156            }
 157
 158            // UPDATE
 18159            foreach (var (key, dest) in destMap)
 160            {
 6161                if (sourceMap.TryGetValue(key, out var src))
 3162                    update(dest, src);
 163            }
 164
 165            // ADD
 18166            foreach (var (key, src) in sourceMap)
 167            {
 6168                if (!destMap.ContainsKey(key))
 3169                    add(src);
 170            }
 3171        }
 172    }
 173
 174    /// <summary>
 175    /// Replaces the contents of the list with the specified items. The list is cleared and the items are added in order
 176    /// </summary>
 177    /// <param name="list">The list to replace the contents of.</param>
 178    /// <param name="items">The items to replace the list with.</param>
 179    /// <typeparam name="T">The type of the elements in the list.</typeparam>
 180    private static void Replace<T>(IList<T> list, T[] items)
 181    {
 6182        list.Clear();
 48183        foreach (var item in items)
 18184            list.Add(item);
 6185    }
 186}
 187