Open
Description
I propose to add a new Elements
extension that accepts System.Range
and which will allow usage of the range operator ..
in C# to succinctly express slicing with start and end offsets.
We already have Slice
, which is why I propose to call this extension Elements
. An overload of Slice
would be confusing since it takes a count as the second argument as opposed to an index.
Prototype
public static IEnumerable<T> Elements<T>(this IEnumerable<T> source, Range range) =>
((range.Start.Value, range.Start.IsFromEnd), (range.End.Value, range.End.IsFromEnd)) switch
{
((0, false), (0, true)) => source,
((var rs, false), (0, true)) => source.Skip(rs),
((0, false), (var re, true)) => source.SkipLast(re),
((var rs, true), (0, true)) => source.TakeLast(rs),
((var rs, false), (var re, false)) =>
source.Index()
.SkipWhile(e => e.Key < rs)
.TakeWhile(e => e.Key < re)
.Select(e => e.Value),
((var rs, true), (var re, true)) =>
source.TakeLast(rs)
.Take(rs - re),
((var rs, true), (var re, false)) =>
source.Index()
.TakeLast(rs)
.TakeWhile(e => e.Key < re)
.Select(e => e.Value),
((var rs, false), (var re, true)) =>
source.Index()
.CountDown(re, (e, cd) => cd is null ? (true, e) : (false, e))
.TakeWhile(e => e is (true, _))
.Choose(e => e)
.Skip(rs)
.Select(e => e.Value)
};
Examples
var seq = Enumerable.Range(0, 10);
var q =
from r in new[]
{
..,
5..,
..5,
3..7,
..^3,
^3..,
^8..^2,
^8..6,
2..^2,
..^20,
20..,
}
select $"{r,6} = [{seq.Elements(r).ToDelimitedString(", ")}]";
foreach (var e in q)
Console.WriteLine(e);
Output:
0..^0 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5..^0 = [5, 6, 7, 8, 9]
0..5 = [0, 1, 2, 3, 4]
3..7 = [3, 4, 5, 6]
0..^3 = [0, 1, 2, 3, 4, 5, 6]
^3..^0 = [7, 8, 9]
^8..^2 = [2, 3, 4, 5, 6, 7]
^8..6 = [2, 3, 4, 5]
2..^2 = [2, 3, 4, 5, 6, 7]
0..^20 = []
20..^0 = []