using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
namespace ICD.Common.Utils.Extensions
{
public static class EnumerableExtensions
{
///
/// Returns the first item in the sequence. Returns the provided default item if there
/// are no elements in the sequence.
///
///
///
///
///
public static T FirstOrDefault(this IEnumerable extends, T defaultItem)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.FirstOrDefault(i => true, defaultItem);
}
///
/// Returns the first element in the sequence matching the predicate. Returns the provided
/// default item if there are no elements matching the predicate in the sequence.
///
///
///
///
///
///
public static T FirstOrDefault(this IEnumerable extends, Func predicate, T defaultItem)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (predicate == null)
throw new ArgumentNullException("predicate");
foreach (T item in extends.Where(predicate))
return item;
return defaultItem;
}
///
/// Returns true if there is at least 1 item in the sequence.
///
///
///
/// Outputs the first item in the sequence.
///
public static bool TryFirst(this IEnumerable extends, out T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.TryFirst(i => true, out item);
}
///
/// Returns true if there is at least 1 item in the sequence.
///
///
///
///
/// Outputs the first item in the sequence.
///
public static bool TryFirst(this IEnumerable extends, Func predicate, out T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (predicate == null)
throw new ArgumentNullException("predicate");
item = default(T);
using (IEnumerator iterator = extends.GetEnumerator())
{
while (iterator.MoveNext())
{
if (!predicate(iterator.Current))
continue;
item = iterator.Current;
return true;
}
}
return false;
}
///
/// Returns the true if an element with the given index is in the sequence.
///
///
///
///
///
///
public static bool TryElementAt(this IEnumerable extends, int index, out T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
item = default(T);
if (index < 0)
return false;
T[] itemArray = extends as T[] ?? extends.ToArray();
if (index >= itemArray.Length)
return false;
item = itemArray[index];
return true;
}
///
/// Compares two sequences for identical values, ignoring order.
///
///
///
///
///
public static bool ScrambledEquals(this IEnumerable extends, IEnumerable other)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends == null)
throw new ArgumentNullException("other");
return extends.ScrambledEquals(other, EqualityComparer.Default);
}
///
/// Compares two sequences for identical values, ignoring order.
///
///
///
///
///
///
public static bool ScrambledEquals(this IEnumerable extends, IEnumerable other, IEqualityComparer comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends == null)
throw new ArgumentNullException("other");
if (comparer == null)
throw new ArgumentNullException("comparer");
Dictionary count = new Dictionary(comparer);
foreach (T item in extends)
{
if (count.ContainsKey(item))
count[item]++;
else
count.Add(item, 1);
}
foreach (T item in other)
{
if (count.ContainsKey(item))
count[item]--;
else
return false;
}
return count.Values.All(c => c == 0);
}
///
/// Returns the index that matches the predicate.
///
///
///
///
///
public static int FindIndex(this IEnumerable extends, Predicate match)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (match == null)
throw new ArgumentNullException("match");
int index = 0;
foreach (T item in extends)
{
if (match(item))
return index;
index++;
}
return -1;
}
///
/// Allows for selection of multiple results for each item in the sequence.
///
///
///
///
///
///
public static IEnumerable SelectMulti(this IEnumerable extends,
params Func[] selectors)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (selectors == null)
throw new ArgumentNullException("selectors");
return extends.SelectMany(source => selectors, (source, selector) => selector(source));
}
///
/// Enumerates each item in the sequence.
/// Useful for processing a LINQ query without creating a new collection.
///
///
///
[PublicAPI]
public static void Execute(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
extends.ForEach(item => { });
}
///
/// Performs the action for each item in the sequence.
///
///
///
///
[PublicAPI]
public static void ForEach(this IEnumerable extends, Action action)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (action == null)
throw new ArgumentNullException("action");
extends.ForEach((item, index) => action(item));
}
///
/// Performs the action for each item in the sequence.
///
///
///
///
[PublicAPI]
public static void ForEach(this IEnumerable extends, Action action)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (action == null)
throw new ArgumentNullException("action");
int index = 0;
foreach (T item in extends)
action(item, index++);
}
#if SIMPLSHARP
///
/// Prepends the item to the start of the sequence.
///
///
///
///
///
public static IEnumerable Prepend(this IEnumerable extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.PrependMany(new[] {item});
}
#endif
///
/// Prepends the items to the start of the sequence.
///
///
///
///
///
[PublicAPI]
public static IEnumerable PrependMany(this IEnumerable extends, params T[] items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (items == null)
throw new ArgumentNullException("items");
foreach (T item in items)
yield return item;
foreach (T each in extends)
yield return each;
}
#if SIMPLSHARP
///
/// Appends the item to the end of the sequence.
///
///
///
///
///
public static IEnumerable Append(this IEnumerable extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.AppendMany(new[] {item});
}
#endif
///
/// Appends the items to the end of the sequence.
///
///
///
///
///
[PublicAPI]
public static IEnumerable AppendMany(this IEnumerable extends, params T[] items)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (items == null)
throw new ArgumentNullException("items");
foreach (T each in extends)
yield return each;
foreach (T item in items)
yield return item;
}
///
/// Default ordering for the items in the sequence.
///
///
///
///
public static IEnumerable Order(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.OrderBy(i => i);
}
///
/// Returns every item in the sequence except the given item.
///
///
///
///
///
public static IEnumerable Except(this IEnumerable extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Except(new[] {item});
}
///
/// Returns the sequence as a IcdHashSet.
///
///
///
///
public static IcdHashSet ToHashSet(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return new IcdHashSet(extends);
}
///
/// Returns the sequence as an index:value dictionary.
///
///
///
///
[PublicAPI]
public static Dictionary ToDictionary(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
Dictionary output = new Dictionary();
extends.ForEach((item, index) => output.Add(index, item));
return output;
}
///
/// Returns the sequence as an index:value dictionary.
///
///
///
///
[PublicAPI]
public static Dictionary ToDictionaryUInt(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
Dictionary output = new Dictionary();
extends.ForEach((item, index) => output.Add((uint)index, item));
return output;
}
///
/// Turns an enumerable of KeyValuePairs back into a dictionary
///
///
///
///
///
public static Dictionary ToDictionary(this IEnumerable> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.ToDictionary(x => x.Key, x => x.Value);
}
///
/// Returns other if the list is empty.
/// Returns other if the list is non-empty and there are two different elements.
/// Returns the element of the list if it is non-empty and all elements are the same.
///
///
///
///
[PublicAPI]
public static T Unanimous(this IEnumerable extends, T other)
{
if (extends == null)
throw new ArgumentNullException("extends");
T[] array = extends as T[] ?? extends.ToArray();
return array.Unanimous() ? array.First() : other;
}
///
/// Returns false if the list is empty.
/// Returns false if the list is non-empty and there are two different elements.
/// Returns true if the list is non-empty and all elements are the same.
///
///
///
[PublicAPI]
public static bool Unanimous(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
T[] array = extends as T[] ?? extends.ToArray();
if (array.Length == 0)
return false;
T val = array.First();
return array.All(x => EqualityComparer.Default.Equals(x, val));
}
///
/// Partitions a sequence into sequences of the given length.
///
///
///
///
[PublicAPI]
public static IEnumerable> Partition(this IEnumerable extends, int partitionSize)
{
if (extends == null)
throw new ArgumentNullException("extends");
using (IEnumerator enumerator = extends.GetEnumerator())
{
while (enumerator.MoveNext())
yield return YieldBatchElements(enumerator, partitionSize - 1);
}
}
private static IEnumerable YieldBatchElements(IEnumerator source, int partitionSize)
{
if (source == null)
throw new ArgumentNullException("source");
// Important to enumerate through partitionSize items before returning
// Otherwise enumerable.Partition(3).Skip(1) will do unwanted things.
List output = new List {source.Current};
for (int i = 0; i < partitionSize && source.MoveNext(); i++)
output.Add(source.Current);
return output;
}
///
/// Returns the minimal element of the given sequence, based on
/// the given projection.
///
///
/// If more than one element has the minimal projected value, the first
/// one encountered will be returned. This overload uses the default comparer
/// for the projected type. This operator uses immediate execution, but
/// only buffers a single result (the current minimal element).
///
/// Type of the source sequence
/// Type of the projected element
/// Source sequence
/// Selector to use to pick the results to compare
/// The minimal element, according to the projection.
/// or is null
/// is empty
[PublicAPI]
public static TSource MinBy(this IEnumerable source,
Func selector)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
return source.MinBy(selector, Comparer.Default);
}
///
/// Returns the minimal element of the given sequence, based on
/// the given projection and the specified comparer for projected values.
///
///
/// If more than one element has the minimal projected value, the first
/// one encountered will be returned. This operator uses immediate execution, but
/// only buffers a single result (the current minimal element).
///
/// Type of the source sequence
/// Type of the projected element
/// Source sequence
/// Selector to use to pick the results to compare
/// Comparer to use to compare projected values
/// The minimal element, according to the projection.
/// ,
/// or is null
/// is empty
[PublicAPI]
public static TSource MinBy(this IEnumerable source, Func selector,
IComparer comparer)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
if (comparer == null)
throw new ArgumentNullException("comparer");
using (IEnumerator sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
throw new InvalidOperationException("Sequence contains no elements");
TSource min = sourceIterator.Current;
TKey minKey = selector(min);
while (sourceIterator.MoveNext())
{
TSource candidate = sourceIterator.Current;
TKey candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) >= 0)
continue;
min = candidate;
minKey = candidateProjected;
}
return min;
}
}
///
/// Removes any null elements from an enumerable of nullable value types
///
///
///
///
[PublicAPI]
public static IEnumerable ExceptNulls(this IEnumerable extends)
where T : struct
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Where(e => e.HasValue).Select(e => e.Value);
}
///
/// Computes the sum of a sequence of byte values.
///
///
///
[PublicAPI]
public static byte Sum(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return (byte)extends.Select(i => (int)i).Sum();
}
///
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
///
///
///
///
[PublicAPI]
public static IEnumerable Consolidate(this IEnumerable extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return Consolidate(extends, Comparer.Default);
}
///
/// Skips duplicate, consecutive items.
/// E.g.
/// [1, 2, 2, 3, 1, 1]
/// Becomes
/// [1, 2, 3, 1]
///
///
///
///
///
[PublicAPI]
public static IEnumerable Consolidate(this IEnumerable extends, IComparer comparer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (comparer == null)
throw new ArgumentNullException("comparer");
bool first = true;
T last = default(T);
foreach (T item in extends)
{
if (!first && comparer.Compare(last, item) == 0)
continue;
first = false;
last = item;
yield return item;
}
}
///
/// Returns true if all items in the sequence match the predicate.
/// Returns false if the sequence is empty.
///
///
///
///
///
[PublicAPI]
public static bool AnyAndAll(this IEnumerable extends, Func predicate)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (predicate == null)
throw new ArgumentNullException("predicate");
bool any = false;
foreach (T item in extends)
{
any = true;
if (!predicate(item))
return false;
}
return any;
}
}
}