views:

142

answers:

3

I have an IEnumerable and I want to get a new IEnumerable containing every nth element.

Can this be done in Linq?

+14  A: 

Just figured it out myself...

The IEnumerable<T>.Where() method has an overload that takes the index of the current element - just what the doctor ordered.

(new []{1,2,3,4,5}).Where((elem, idx) => idx % 2 == 0);

This would return

{1, 3, 5}

Update: In order to cover both my use case and Dan Tao's suggestion, let's also specify what the first returned element should be:

var firstIdx = 1;
var takeEvery = 2;
var list =  new []{1,2,3,4,5};

var newList = list
    .Skip(firstIdx)
    .Where((elem, idx) => idx % takeEvery == 0);

...would return

{2, 4}
Cristi Diaconescu
First time I've seen a really solid use for the index in a collection predicate, bravo, I wouldn't have thought this up.
Jimmy Hoffa
You could define an extension method which returns this.Where(...) to make this clearer in your app itself.
Douglas
I might suggest using `idx + 1` to match the description of "every Nth element"; at least to me, this suggests that the first element returned should be the Nth element. So every 2nd element in `{1,2,3,4,5}` -- to *me* -- means `{2,4}` (your code returns `{1,3,5}`). Maybe this is subjective, though.
Dan Tao
@Dan Tao: Ever heard this one? "Why are a dog and an engineer alike? - They both have intelligent eyes, and neither one can talk". My code does what I want it to do, I just failed to explain it correctly :)
Cristi Diaconescu
+6  A: 

To implement Cristi's suggestion:

public static IEnumerable<T> Sample<T>(this IEnumerable<T> source, int interval)
{
    // null check, out of range check go here

    return source.Where((value, index) => (index + 1) % interval == 0);
}

Usage:

var upToTen = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var evens = upToTen.Sample(2);
var multiplesOfThree = upToTen.Sample(3);
Dan Tao
You could also skip the array and use `Enumerable.Range(1,10)`.
Anthony Pegram
@Anthony: It's just an example, intended for immediate comprehension. I know some developers would have to look up `Enumerable.Range`.
Dan Tao
A: 

While not LINQ you may also create an extension method with yield.

public static IEnumerable<T> EverySecondObject<T>(this IEnumerable<T> list)
{
    using (var enumerator = list.GetEnumerator())
    {
        while (true)
        {
            if (!enumerator.MoveNext())
                yield break;
            if (enumerator.MoveNext())
                yield return enumerator.Current;
            else
                yield break;
        }
    }
}
Albin Sunnanbo
Wrap that in a `using`! (`IEnumerable` inherits `IDisposable`.)
Dan Tao
@Dan: You mean IEnumerator. How come I never realized that before? <digging through some old code />
Albin Sunnanbo
@Albin: Yes, I meant `IEnumerator`. Whoops! (For some reason I always type one when I mean to type the other. Must be muscle memory.)
Dan Tao
Strange, IEnumerator<T> inherits IDisposable, but IEnumerator does not. Wonder why.
Albin Sunnanbo