In Ruby there is an each_cons
on Enumerable. It works like this
(1..5).each_cons(3) {|n| p n}
[1, 2, 3]
[2, 3, 4] [3, 4, 5]
I would like to do this in C#. LINQ would be nice.
The following does something similar but it loops one 开发者_如何学运维to many and it's also hardcoded to return only two elements
var ints = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var cons = ints.Select((o, i) =>
new int[]{ ints[i], i == ints.Length - 1 ? 0 : ints[i + 1] });
It would be nice if it could be created as an iterator over the original array instead of having to create a lot of arrays.
Try the following
var ints = Enumerable.Range(1, 3).Select(x => Enumerable.Range(x, 3));
This will return an IEnumerable<IEnumerable<int>>
with the specified values. You can add the .ToArray
expression at any point to get it into an array if that's the intent (can't tell if that's whatthe []
mean in ruby)
You can create an extension method that achieves it in this way:
public static IEnumerable<IEnumerable<T>> each_cons<T>(this IEnumerable<T> enumerable, int length)
{
for (int i = 0; i < enumerable.Count() - length + 1; i++)
{
yield return enumerable.Skip(i).Take(length);
}
}
Consume it:
var ints = Enumerable.Range(1, 7).each_cons(3);
Here's a generic extension method that turned out to be way to complicated for my current use case but it seems to work.
static class EnumerableExtension
{
public static IEnumerable<IEnumerable<T>> EachCons<T>(this IEnumerable<T> sequence, int cons)
{
for (IEnumerable<T> head = sequence.Take(cons), tail = sequence.Skip(1);
head.Count() == cons; head = tail.Take(cons), tail = tail.Skip(1))
yield return head;
}
}
Use Jay's implementation instead. It's MUCH faster. I just left this one here because it's mentioned in Jay's answer.
精彩评论