Skip to content

Instantly share code, notes, and snippets.

@shanecelis
Last active November 27, 2017 14:01
Show Gist options
  • Save shanecelis/39ae12fe8f3392716470b6cc6d1c1e68 to your computer and use it in GitHub Desktop.
Save shanecelis/39ae12fe8f3392716470b6cc6d1c1e68 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
/*
Some helpful Linq extensions I've used with Unity.
Original code from stackoverflow.com where user contributions are
licensed under CC-BY-SA 3.0 with attribution required.
*/
public static class LinqExtensions {
// http://stackoverflow.com/questions/3645644/whats-your-favorite-linq-to-objects-operator-which-is-not-built-in
/// <summary>Creates a <see cref="Queue&lt;T&gt;"/> from an enumerable
/// collection.</summary>
public static Queue<T> ToQueue<T>(this IEnumerable<T> source) {
if (source == null)
throw new ArgumentNullException("source");
return new Queue<T>(source);
}
/// <summary>Creates a <see cref="Stack&lt;T&gt;"/> from an enumerable
/// collection.</summary>
public static Stack<T> ToStack<T>(this IEnumerable<T> source) {
if (source == null)
throw new ArgumentNullException("source");
return new Stack<T>(source);
}
public static bool IsEmpty<T>(this IEnumerable<T> source) {
return !source.Any();
}
public static void Each<T>(this IEnumerable<T> items, Action<T> action) {
foreach (var i in items)
action(i);
}
/// <summary>Adds a single element to the end of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing all the input elements, followed by the
/// specified additional element.</returns>
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T element) {
if (source == null)
throw new ArgumentNullException("source");
return concatIterator(element, source, false);
}
/// <summary>Adds a single element to the start of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing the specified additional element, followed by
/// all the input elements.</returns>
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> tail, T head) {
if (tail == null)
throw new ArgumentNullException("tail");
return concatIterator(head, tail, true);
}
private static IEnumerable<T> concatIterator<T>(T extraElement,
IEnumerable<T> source,
bool insertAtStart) {
if (insertAtStart)
yield return extraElement;
foreach (var e in source)
yield return e;
if (!insertAtStart)
yield return extraElement;
}
// http://stackoverflow.com/questions/10206557/c-sharp-cast-dictionarystring-anytype-to-dictionarystring-object-involvin
public static IEnumerable<DictionaryEntry> CastDict(this IEnumerable dictionary) {
foreach (DictionaryEntry entry in dictionary)
{
yield return entry;
}
}
// https://code.msdn.microsoft.com/LINQ-to-DataSets-Custom-41738490
public static IEnumerable<T2> Combine<T,T2>(this IEnumerable<T> first,
IEnumerable<T> second,
System.Func<T,T,T2> func) {
using (IEnumerator<T> e1 = first.GetEnumerator(),
e2 = second.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
yield return func(e1.Current, e2.Current);
}
}
}
// http://stackoverflow.com/questions/3173718/how-to-get-a-random-object-using-linq
public static T Random<T>(this IEnumerable<T> enumerable) {
var r = new Random();
var list = enumerable as IList<T> ?? enumerable.ToList();
return list.ElementAt(r.Next(0, list.Count()));
}
// Reservoir Sampling
// https://en.wikipedia.org/wiki/Reservoir_sampling
/*
Return an IEnumerable with k random items from the given
enumerable with n items.
It's O(n) and doesn't need to get the count n first.
Note: The if the enumerable only has k items, the result will not
be shuffled.
*/
public static IEnumerable<T> Random<T>(this IEnumerable<T> enumerable,
int k) {
var list = new List<T>(k);
using (var e = enumerable.GetEnumerator()) {
for (int i = 0; i < k && e.MoveNext(); i++)
list[i] = e.Current;
var r = new Random();
int j;
for (int i = k + 1; e.MoveNext(); i++) {
j = r.Next(0, i); // [0, i)
if (j < k)
list[j] = e.Current;
}
return list;
}
}
/*
Behaves like .Skip(count) unless that would skip all the elements, in which
case it returns the last element.
new int[] {0, 1, 2, 3, 4}.Skip(10) -> { }
new int[] {0, 1, 2, 3, 4}.SkipUnlessLast(10) -> { 4 }
But otherwise behaves the same.
new int[] {0, 1, 2, 3, 4}.Skip(2) -> { 2, 3, 4 }
new int[] {0, 1, 2, 3, 4}.SkipUnlessLast(2) -> { 2, 3, 4 }
*/
public static IEnumerable<T> SkipUnlessLast<T>(this IEnumerable<T> source, int count) {
// https://blogs.msmvps.com/jonskeet/2011/01/02/reimplementing-linq-to-objects-part-23-take-skip-takewhile-skipwhile/
T lastItem = default(T);
using (IEnumerator<T> iterator = source.GetEnumerator()) {
for (int i = 0; i < count; i++) {
if (! iterator.MoveNext()) {
if (i != 0)
yield return lastItem;
yield break;
} else {
lastItem = iterator.Current;
}
}
bool anyMore = false;
while (iterator.MoveNext()) {
anyMore = true;
yield return iterator.Current;
}
if (! anyMore)
yield return lastItem;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment