Skip to content

Instantly share code, notes, and snippets.

@kpol
Last active February 6, 2018 22:14
Show Gist options
  • Save kpol/ba05486f5477bb5a99daa113e64009b8 to your computer and use it in GitHub Desktop.
Save kpol/ba05486f5477bb5a99daa113e64009b8 to your computer and use it in GitHub Desktop.
Enumerable Extensions
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace LinqExtensions
{
public static class EnumerableExtensions
{
/// <summary>
/// Returns shuffled elements from a sequence (Fisher-Yates shuffle).
/// </summary>
/// <typeparam name="T">The type of the elements of source.</typeparam>
/// <param name="source">A sequence of values that needs to be shuffled.</param>
/// <returns>An <see cref="IEnumerable{T}"/> that contains shuffled elements of the input sequence.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
var array = source.ToArray();
var random = GlobalRandom.Instance;
for (var i = array.Length - 1; i >= 0; i--)
{
var index = random.Next(0, i + 1);
yield return array[index];
array[index] = array[i];
// T tmp = array[i];
// array[i] = array[index];
// array[index] = tmp;
}
}
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
return source.MaxBy(selector, Comparer<TKey>.Default);
}
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var max = sourceIterator.Current;
var maxKey = selector(max);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, maxKey) > 0)
{
max = candidate;
maxKey = candidateProjected;
}
}
return max;
}
}
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
return source.MinBy(selector, Comparer<TKey>.Default);
}
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var min = sourceIterator.Current;
var minKey = selector(min);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) < 0)
{
min = candidate;
minKey = candidateProjected;
}
}
return min;
}
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
return source.DistinctBy(selector, EqualityComparer<TKey>.Default);
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (selector == null) throw new ArgumentNullException(nameof(selector));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
var set = new HashSet<TKey>(comparer);
return source.Where(item => set.Add(selector(item)));
}
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (count <= 0)
{
yield break;
}
var list = source as IReadOnlyList<T>;
if (list != null)
{
var c = count > list.Count ? list.Count : count;
for (int i = list.Count - c; i < list.Count; i++)
{
yield return list[i];
}
}
else
{
var q = new Queue<T>(count);
foreach (var item in source)
{
if (q.Count == count)
{
q.Dequeue();
}
q.Enqueue(item);
}
foreach (var item in q)
{
yield return item;
}
}
}
}
public class GlobalRandom : Random
{
private static int _seed = Environment.TickCount;
private static readonly ThreadLocal<Random> Random =
new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref _seed)));
private GlobalRandom() { }
public static Random Instance => new GlobalRandom();
public override int Next() => Random.Value.Next();
public override int Next(int minValue, int maxValue) => Random.Value.Next(minValue, maxValue);
public override int Next(int maxValue) => Random.Value.Next(maxValue);
public override double NextDouble() => Random.Value.NextDouble();
public override void NextBytes(byte[] buffer) => Random.Value.NextBytes(buffer);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment