Reputation: 250
Say you've got some IEnumerable called S of length N. I would like to select all continuous subsequences of length n <= N from S.
If S were, say, a string, this'd be pretty easy. There are (S.Length - n + 1) subsequences of length n. For example, "abcdefg" is length (7), so that means it has (5) substrings of length (3): "abc", "bcd", "cde", "def", "efg".
But S could be any IEnumerable, so this route isn't open. How do I use extension methods to solve this?
Upvotes: 4
Views: 2606
Reputation: 27852
For future readers.
Here is a small example.
private static void RunTakeSkipExample()
{
int takeSize = 10; /* set takeSize to 10 */
/* create 25 exceptions, so 25 / 10 .. means 3 "takes" with sizes of 10, 10 and 5 */
ICollection<ArithmeticException> allArithExceptions = new List<ArithmeticException>();
for (int i = 1; i <= 25; i++)
{
allArithExceptions.Add(new ArithmeticException(Convert.ToString(i)));
}
int counter = 0;
IEnumerable<ArithmeticException> currentTakeArithExceptions = allArithExceptions.Skip(0).Take(takeSize);
while (currentTakeArithExceptions.Any())
{
Console.WriteLine("Taking! TakeSize={0}. Counter={1}. Count={2}.", takeSize, (counter + 1), currentTakeArithExceptions.Count());
foreach (ArithmeticException ae in currentTakeArithExceptions)
{
Console.WriteLine(ae.Message);
}
currentTakeArithExceptions = allArithExceptions.Skip(++counter * takeSize).Take(takeSize);
}
}
The output:
Taking! TakeSize=10. Counter=1. Count=10.
1
2
3
4
5
6
7
8
9
10
Taking! TakeSize=10. Counter=2. Count=10.
11
12
13
14
15
16
17
18
19
20
Taking! TakeSize=10. Counter=3. Count=5.
21
22
23
24
25
You can see by the .Message to each exception to verify each distinct exception was "taken".
And now, a movie quote!
But what I do have are a very particular set of skills; skills I have acquired over a very long career. Skills that make me a nightmare for people like you.
Upvotes: 0
Reputation: 18203
Here is a new extension method to do what you want in C#
static IEnumerable<IEnumerable<T>> Subseqs<T>(this IEnumerable<T> xs, int n)
{
var cnt = xs.Count() - n;
Enumerable.Range(0, cnt < 0 ? 0 : cnt).Select(i => xs.Skip(i).Take(n));
}
Upvotes: 0
Reputation: 18749
IEnumerable<IEnumerable<T>> ContiguousSubseqences<T>(this IEnumerable<T> seq, Func<T,bool> constraint)
{
int i = 0;
foreach (T t in seq)
{
if (constraint(t))
yield return seq.Skip(i).TakeWhile(constraint);
i++;
}
}
Upvotes: 0
Reputation: 700252
You can use the Select extension that provides the index to create an object containing the index and value, then divide the index with the length to place them into groups:
var x = values.Select((n, i) => new { Index = i, Value = n }).GroupBy(a => a.Index / 3);
Upvotes: 0
Reputation: 118865
F# has a library function called Seq.windowed for this.
// windowed : int -> seq<'a> -> seq<array<'a>>
let windowed n (s: seq<_>) =
if n <= 0 then Helpers.invalid_arg2 "n" "the window size must be positive"
{ let arr = Array.zero_create n
let r = ref (n-1)
let i = ref 0
use e = s.GetEnumerator()
while e.MoveNext() do
do arr.[!i] <- e.Current
do i := (!i + 1) % n
if !r = 0 then
yield Array.init n (fun j -> arr.[(!i+j) % n])
else
do r := (!r - 1) }
Upvotes: 4
Reputation: 13025
Actually, you can use LINQ to solve this problem, e.g.
var subList = list.Skip(x).Take(y);
where list is an IEnumerable
Upvotes: 2