using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Utilities
{
///
/// Extensions for that don't currently exist in System.Linq.
///
public static class EnumerableExtensions
{
///
/// Adds a value to the beginning of the sequence.
///
/// the type of the elements of
/// a sequence of values
/// the value to prepend to
/// a new sequence beginning with
public static IEnumerable Prepend(this IEnumerable seq, T prep)
=> new PrependEnumerable(seq, prep);
private sealed class PrependEnumerable : IEnumerable
{
private readonly IEnumerable rest;
private readonly T first;
public PrependEnumerable(IEnumerable rest, T first)
{
this.rest = rest;
this.first = first;
}
public PrependEnumerator GetEnumerator() => new PrependEnumerator(this);
public struct PrependEnumerator : IEnumerator
{
private readonly IEnumerator restEnum;
private readonly PrependEnumerable enumerable;
private int state;
internal PrependEnumerator(PrependEnumerable enumerable)
{
this.enumerable = enumerable;
restEnum = enumerable.rest.GetEnumerator();
state = 0;
Current = default;
}
public T Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose() => restEnum.Dispose();
public bool MoveNext()
{
switch (state)
{
case 0:
Current = enumerable.first;
state++;
return true;
case 1:
if (!restEnum.MoveNext())
{
state = 2;
return false;
}
else
Current = restEnum.Current;
return true;
case 2:
default:
return false;
}
}
public void Reset()
{
restEnum.Reset();
state = 0;
}
}
IEnumerator IEnumerable.GetEnumerator() => new PrependEnumerator(this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
///
/// Adds a value to the end of the sequence.
///
/// the type of the elements of
/// a sequence of values
/// the value to append to
/// a new sequence ending with
public static IEnumerable Append(this IEnumerable seq, T app)
=> new AppendEnumerable(seq, app);
private sealed class AppendEnumerable : IEnumerable
{
private readonly IEnumerable rest;
private readonly T last;
public AppendEnumerable(IEnumerable rest, T last)
{
this.rest = rest;
this.last = last;
}
public AppendEnumerator GetEnumerator() => new AppendEnumerator(this);
public struct AppendEnumerator : IEnumerator
{
private readonly IEnumerator restEnum;
private readonly AppendEnumerable enumerable;
private int state;
internal AppendEnumerator(AppendEnumerable enumerable)
{
this.enumerable = enumerable;
restEnum = enumerable.rest.GetEnumerator();
state = 0;
Current = default;
}
public T Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose() => restEnum.Dispose();
public bool MoveNext()
{
switch (state)
{
case 0:
if (!restEnum.MoveNext())
{
state = 1;
goto case 1;
}
else
Current = restEnum.Current;
return true;
case 1:
Current = enumerable.last;
state++;
return true;
case 2:
default:
return false;
}
}
public void Reset()
{
restEnum.Reset();
state = 0;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
///
/// LINQ-style extension method that filters elements out of an enumeration.
///
/// the type of the enumeration
/// the enumeration to filter
/// a filtered enumerable
public static IEnumerable NonNull(this IEnumerable self) where T : class
=> self.Where(o => o != null);
///
/// LINQ-style extension method that filters elements out of an enumeration based on a converter.
///
/// the type of the enumeration
/// the type to compare to null
/// the enumeration to filter
/// the predicate to select for filtering
/// a filtered enumerable
public static IEnumerable NonNull(this IEnumerable self, Func pred) where U : class
=> self.Where(o => pred(o) != null);
///
/// LINQ-style extension method that filters elements from an enumeration of nullable types.
///
/// the underlying type of the nullable enumeration
/// the enumeration to filter
/// a filtered enumerable
public static IEnumerable NonNull(this IEnumerable self) where T : struct
=> self.Where(o => o != null).Select(o => o.Value);
///
/// LINQ-style extension method that filters elements out of an enumeration based on a converter to a nullable type.
///
/// the type of the enumeration
/// the type of the predicate's resulting nullable
/// the enumeration to filter
/// the predicate to select for filtering
/// a filtered enumerable
public static IEnumerable NonNull(this IEnumerable self, Func pred) where U : struct
=> self.Where(o => pred(o) != null);
}
}