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