tags:

views:

1129

answers:

7

I'm using .NET 3.5 and would like to be able to obtain every nth item from a List. I'm not bothered as to whether it's achieved using a lambda expression or LINQ.

Edit

Looks like this question provoked quite a lot of debate (which is a good thing, right?). The main thing I've learnt is that when you think you know every way to do something (even as simple as this), think again!

+20  A: 

I know it's "old school," but why not just use a for loop with stepping = n?

Michael Todd
That was basically my thought.
Mark Pim
@Michael Todd: It works, but the problem with that is that you have to duplicate that functionality everywhere. By using LINQ, it becomes part of the composed query.
casperOne
I don't think that's the point of this question, though
Sung Meister
Hmm...haven't used LINQ, yet. I guess I should get started....
Michael Todd
@casperOne: I believe programmers invented this thing called subroutines to deal with this ;) In a real program I'd probably use a loop, despite the clever LINQ version, since a loop means you don't have to iterate over every element (increment the index by N.)
mquander
@mquander amen brother.
Michael Meadows
I agree go for the old school solution, and I'm even guessing this will perform better.
Arkain
+7  A: 

For Loop

for(int i = 0; i < list.Count; i += n)
    //Nth Item..
Quintin Robinson
+28  A: 
return list.Where((x, i) => i % nStep == 0);
mquander
@mquander: Note that this will actually give you the nth - 1 element. If you want the actual nth elements (skipping the first) then you will have to add 1 to i.
casperOne
Yes, I suppose it sort of depends what you mean by "nth," but your interpretation might be more common. Add or subtract from i to suit your needs.
mquander
Just to note: The Linq/Lambda solution will be much less performant than a simple loop with fixed increment.
MartinStettner
But it's cooler!
TheTXI
Martin: absolutely. This is neat but a loop is better.
mquander
Seems like a lot of questions are geared toward the sexy solution over the practical one. Not using a traditional loop will force iteration twice (once to build the list, and once to consume it), but most questioners get defensive when you point it out. I throw up my hands in frustration.
Michael Meadows
Not necessarily, with deferred execution it could be used in a foreach loop and only loops over the original list once.
Samuel
It depends what you mean by "practical." If you need a quick way to get every other item in a list of 30 items when the user clicks a button, I'd say this is every bit as practical. Sometimes performance really doesn't matter anymore. Of course, sometimes it does.
mquander
By practical, I mean that it can be maintained by developers who will never care enough about their art to have philosophical discussions about what practical means through SO comments.
Michael Meadows
I don't really find the above expression cryptic at all (and I would expect my current coworkers to understand it), but I agree that if your audience and code base follow a more procedural style, you should endeavour to write whatever will be least confusing.
mquander
I asked this question purely to learn, above anything else. Noted that other methods exist - I know the foreach method, but I didn't know this one :)
Paul Suart
+12  A: 

Sounds like

IEnumerator<T> GetNth<T>(List<T> list, int n) {
  for (int i=0; i<list.Count; i+=n)
    yield return list[i]
}

would do the trick. I do not see the need to use Linq or a lambda expressions.

EDIT:

Make it

public static class MyListExtensions {
  public static IEnumerable<T> GetNth<T>(this List<T>, int n) {
    for (int i=0; i<list.Count; i+=n)
      yield return list[i]
  }
}

and you write in a LINQish way

from var element in MyList.GetNth(10) select element;

2nd Edit:

To make it even more LINQish

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];
MartinStettner
I like this method for using the this[] getter instead of Where() method, which essentially iterates every element of the IEnumerable. If you have an IList/ICollection type, this is the better approach, IMHO.
spoulson
+7  A: 

You can use the Where overload which passes the index along with the element

var everyFourth = list.Where((x,i) => i % 4 == 0);
JaredPar
Gotta say I'm a fan of this method.
Quintin Robinson
I keep forgetting you can do that - very nice.
Stephen Newman
+1  A: 

I'm not sure if it's possible to do with a LINQ expression, but I know that you can use the Where extension method to do it. For example to get every fifth item:

List<T> list = originalList.Where((t,i) => (i % 5) == 0).ToList();

This will get the first item and every fifth from there. If you want to start at the fifth item instead of the first, you compare with 4 instead of comparing with 0.

Guffa
+2  A: 

MartinStettner has the right idea. Be mindful that Linq is awesome, but division comes at a steep price. This is an example where iteration will be much cheaper then a predicate utilizing division. If linq must be used, the range example MartinStettner supplied would be best as it only requires division once.

Real John Connor