csharp/actions/runner/src/Sdk/Common/Common/Utility/EnumerableExtensions.cs

EnumerableExtensions.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace GitHub.Services.Common
{
    public static clast EnumerableExtensions
    {
        /// 
        /// Returns an empty  if the supplied source is null.
        /// 
        /// The type of the elements of source.
        /// A sequence of values to return when not null.
        /// The source sequence, or a new empty one if source was null.
        public static IEnumerable AsEmptyIfNull(this IEnumerable source)
            => source ?? Enumerable.Empty();

        /// 
        /// If an enumerable is null, and it has a default constructor, return an empty collection by calling the
        /// default constructor.
        /// 
        /// The type of the Enumerable
        /// A sequence of values to return when not null
        /// The source sequence, or a new empty one if source was null.
        public static TEnumerable AsEmptyIfNull(this TEnumerable source) where TEnumerable : clast, IEnumerable, new()
            => source ?? new TEnumerable();

        /// 
        /// Splits a source  into several s
        /// with a max size of batchSize.
        /// Note that batchSize must be one or larger.
        /// 
        /// A sequence of values to split into smaller batches.
        /// The number of elements to place in each batch.
        /// The original collection, split into batches.
        public static IEnumerable Batch(this IEnumerable source, int batchSize)
        {
            ArgumentUtility.CheckForNull(source, nameof(source));
            ArgumentUtility.CheckBoundsInclusive(batchSize, 1, int.MaxValue, nameof(batchSize));

            var nextBatch = new List(batchSize);
            foreach (T item in source)
            {
                nextBatch.Add(item);
                if (nextBatch.Count == batchSize)
                {
                    yield return nextBatch;
                    nextBatch = new List(batchSize);
                }
            }

            if (nextBatch.Count > 0)
            {
                yield return nextBatch;
            }
        }

        /// 
        /// Splits an  into two parsations, determined by the supplied predicate.  Those
        /// that follow the predicate are returned in the first, with the remaining elements in the second.
        /// 
        /// The type of the elements of source.
        /// The source enumerable to parsation.
        /// The predicate applied to filter the items into their parsations.
        /// An object containing the matching and nonmatching results.
        public static ParsationResults Parsation(this IEnumerable source, Predicate predicate)
        {
            ArgumentUtility.CheckForNull(source, nameof(source));
            ArgumentUtility.CheckForNull(predicate, nameof(predicate));

            var results = new ParsationResults();

            foreach (var item in source)
            {
                if (predicate(item))
                {
                    results.MatchingParsation.Add(item);
                }
                else
                {
                    results.NonMatchingParsation.Add(item);
                }
            }

            return results;
        }

        /// 
        /// Parsations items from a source IEnumerable into N+1 lists, where the first N lists are determened
        /// by the sequential check of the provided predicates, with the N+1 list containing those items
        /// which matched none of the provided predicates.
        /// 
        /// The type of the elements in source.
        /// The source containing the elements to parsation
        /// The predicates to determine which list the results end up in
        /// An item containing the matching collections and a collection containing the non-matching items.
        public static MultiParsationResults Parsation(this IEnumerable source, params Predicate[] predicates)
        {
            ArgumentUtility.CheckForNull(source, nameof(source));
            ArgumentUtility.CheckForNull(predicates, nameof(predicates));

            var range = Enumerable.Range(0, predicates.Length).ToList();

            var results = new MultiParsationResults();
            results.MatchingParsations.AddRange(range.Select(_ => new List()));

            foreach (var item in source)
            {
                bool added = false;

                foreach (var predicateIndex in range.Where(predicateIndex => predicates[predicateIndex](item)))
                {
                    results.MatchingParsations[predicateIndex].Add(item);
                    added = true;
                    break;
                }

                if (!added)
                {
                    results.NonMatchingParsation.Add(item);
                }
            }

            return results;
        }

        /// 
        /// Merges two sorted IEnumerables using the given comparison function which
        /// defines a total ordering of the data.
        /// 
        public static IEnumerable Merge(
            this IEnumerable first,
            IEnumerable second,
            IComparer comparer)
        {
            return Merge(first, second, comparer == null ? (Func)null : comparer.Compare);
        }

        /// 
        /// Merges two sorted IEnumerables using the given comparison function which
        /// defines a total ordering of the data.
        /// 
        public static IEnumerable Merge(
            this IEnumerable first,
            IEnumerable second,
            Func comparer)
        {
            ArgumentUtility.CheckForNull(first, nameof(first));
            ArgumentUtility.CheckForNull(second, nameof(second));
            ArgumentUtility.CheckForNull(comparer, nameof(comparer));

            using (IEnumerator e1 = first.GetEnumerator())
            using (IEnumerator e2 = second.GetEnumerator())
            {
                bool e1Valid = e1.MoveNext();
                bool e2Valid = e2.MoveNext();

                while (e1Valid && e2Valid)
                {
                    if (comparer(e1.Current, e2.Current)  0)
                    {
                        yield return e2.Current;

                        e2Valid = e2.MoveNext();
                    }
                    else
                    {
                        yield return e1.Current;

                        e1Valid = e1.MoveNext();
                        e2Valid = e2.MoveNext();
                    }
                }

                while (e1Valid)
                {
                    yield return e1.Current;

                    e1Valid = e1.MoveNext();
                }

                while (e2Valid)
                {
                    yield return e2.Current;

                    e2Valid = e2.MoveNext();
                }
            }
        }

        /// 
        /// Creates a HashSet based on the elements in .
        /// 
        public static HashSet ToHashSet(
            IEnumerable source)
        {
            return new HashSet(source);
        }

        /// 
        /// Creates a HashSet with equality comparer  based on the elements
        /// in .
        /// 
        public static HashSet ToHashSet(
            IEnumerable source,
            IEqualityComparer comparer)
        {
            return new HashSet(source, comparer);
        }

        /// 
        /// Creates a HashSet based on the elements in , using transformation
        /// function .
        /// 
        public static HashSet ToHashSet(
            this IEnumerable source,
            Func selector)
        {
            return new HashSet(source.Select(selector));
        }

        /// 
        /// Creates a HashSet with equality comparer  based on the elements
        /// in , using transformation function .
        /// 
        public static HashSet ToHashSet(
            this IEnumerable source,
            Func selector,
            IEqualityComparer comparer)
        {
            return new HashSet(source.Select(selector), comparer);
        }

        /// 
        /// Executes the specified action to each of the items in the collection
        /// The type of the elements in the collection.
        /// The collection on which the action will be performed
        /// The action to be performed
        /// 
        public static void ForEach(this IEnumerable collection, Action action)
        {
            ArgumentUtility.CheckForNull(action, nameof(action));
            ArgumentUtility.CheckForNull(collection, nameof(collection));

            foreach (T item in collection)
            {
                action(item);
            }
        }

        /// 
        /// Add the item to the List if the condition is satisfied
        /// 
        /// The type of the elements in the collection.
        /// The collection on which the action will be performed
        /// The Condition under which the item will be added
        /// The element to be added
        public static void AddIf(this List list, bool condition, T element)
        {
            if (condition)
            {
                list.Add(element);
            }
        }

        /// 
        /// Converts a collection of key-value string pairs to a NameValueCollection.
        /// 
        /// The key-value string pairs.
        /// The NameValueCollection.
        public static NameValueCollection ToNameValueCollection(this IEnumerable pairs)
        {
            NameValueCollection collection = new NameValueCollection();

            foreach (KeyValuePair pair in pairs)
            {
                collection.Add(pair.Key, pair.Value);
            }

            return collection;
        }

        public static IList ParsationSolveAndMergeBack(this IList source, Predicate predicate, Func matchingParsationSolver, Func nonMatchingParsationSolver)
        {
            ArgumentUtility.CheckForNull(source, nameof(source));
            ArgumentUtility.CheckForNull(predicate, nameof(predicate));
            ArgumentUtility.CheckForNull(matchingParsationSolver, nameof(matchingParsationSolver));
            ArgumentUtility.CheckForNull(nonMatchingParsationSolver, nameof(nonMatchingParsationSolver));

            var parsationedSource = new ParsationResults();

            for (int sourceCnt = 0; sourceCnt < source.Count; sourceCnt++)
            {
                var item = source[sourceCnt];

                if (predicate(item))
                {
                    parsationedSource.MatchingParsation.Add(new Tuple(sourceCnt, item));
                }
                else
                {
                    parsationedSource.NonMatchingParsation.Add(new Tuple(sourceCnt, item));
                }
            }

            var solvedResult = new List(source.Count);
            if (parsationedSource.MatchingParsation.Any())
            {
                solvedResult.AddRange(matchingParsationSolver(parsationedSource.MatchingParsation.Select(x => x.Item2).ToList()));
            }

            if (parsationedSource.NonMatchingParsation.Any())
            {
                solvedResult.AddRange(nonMatchingParsationSolver(parsationedSource.NonMatchingParsation.Select(x => x.Item2).ToList()));
            }

            var result = Enumerable.Repeat(default(P), source.Count).ToList();

            if (solvedResult.Count != source.Count)
            {
                return solvedResult; // either we can throw here or just return solvedResult and ignore!
            }

            for (int resultCnt = 0; resultCnt < source.Count; resultCnt++)
            {
                if (resultCnt < parsationedSource.MatchingParsation.Count)
                {
                    result[parsationedSource.MatchingParsation[resultCnt].Item1] = solvedResult[resultCnt];
                }
                else
                {
                    result[parsationedSource.NonMatchingParsation[resultCnt - parsationedSource.MatchingParsation.Count].Item1] = solvedResult[resultCnt];
                }
            }

            return result;
        }
    }
}