views:

333

answers:

4

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?

+1  A: 

Actually, you can use LINQ to solve this problem, e.g.

var subList = list.Skip(x).Take(y);

where list is an IEnumerable

Julien Poulin
I didn't actually try this, but doesn't that only return the first y elements after the first x elements? We'd need to do the Skip several times. Remember, we need *all* continuous subsequences, not just one of them! :)
sassafrass
yes, you would have to iterate for each subsequence
Julien Poulin
+4  A: 

F# has a library function called Seq.windowed for this.

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.Seq.html

// 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) }
Brian
Awesome! This is pretty close to what I want. I'll see if I can translate this to C#.
sassafrass
A: 

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);
Guffa
That's not quite right. You're really just splitting them up into groups of size N, not producing the individual subsequences. "abcde" with N = 3 should yield "abc", "bcd"< "cde", for example. But your method yields "abc", "de".
sassafrass
A: 
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++;
    }
}
Glenn Slayden