132

I'm using .NET 3.5 and would like to be able to obtain every *n*th 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!

Paul Suart
  • 6,505
  • 7
  • 44
  • 65
  • I didn't edit out any meaning behind your original question; I only cleaned it up and used Capitalization and punctuation correctly. (.NET is capitalized, LINQ is in all-Caps, and it's not a 'lambda', it's a 'lambda expression'.) – George Stocker Mar 25 '09 at 17:44
  • 1
    You replaced "fussed" with "sure" which are not at all synonyms. – mqp Mar 25 '09 at 17:44
  • Would seem so. Having sure doesn't make sense either, unless it's "I'm not sure if it's achievable using..." – Samuel Mar 25 '09 at 17:47
  • Yes, as I understand it, that's about right. – mqp Mar 25 '09 at 17:47
  • fussed would probably be best to be replaced with "concerned" so that it reads "I'm not concerned as to whether it's achieved using a lambda expression or LINQ." – TheTXI Mar 25 '09 at 17:47
  • k. I changed it to fussy so the OP wouldn't get in a tizzy about the meaning being changed. I've never seen the word 'fussed' before. Is it in common usage? – George Stocker Mar 25 '09 at 17:48
  • I only see/hear it rarely in my area and it tends to be more closely related to rural dialects. I can't assume where the OP gets it from. – TheTXI Mar 25 '09 at 17:50
  • I would say it not commonly used. Look at the definition. http://www.thefreedictionary.com/fussed. Usage: thedorko fussed over his post being made more legible. – Samuel Mar 25 '09 at 17:51
  • @Samuel: That is the most proper usage of it, but in the way thedorko was using it, it definitely isn't your standard denotation. Chalk it up to regional dialects and move on. – TheTXI Mar 25 '09 at 17:54
  • My original issue with the edit was the introduction of the phrase "I'm not sure whether it's achieved using a" - this was irrelevant, and not what I meant at all. – Paul Suart Mar 25 '09 at 19:13
  • 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 Mar 26 '09 at 00:12

10 Answers10

227
return list.Where((x, i) => i % nStep == 0);
mqp
  • 70,359
  • 14
  • 95
  • 123
  • 8
    @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 Mar 25 '09 at 17:37
  • 2
    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. – mqp Mar 25 '09 at 17:39
  • 5
    Just to note: The Linq/Lambda solution will be much less performant than a simple loop with fixed increment. – MartinStettner Mar 25 '09 at 17:43
  • 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 Mar 25 '09 at 17:52
  • 5
    Not necessarily, with deferred execution it could be used in a foreach loop and only loops over the original list once. – Samuel Mar 25 '09 at 17:53
  • 2
    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. – mqp Mar 25 '09 at 17:54
  • 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 Mar 25 '09 at 17:59
  • 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. – mqp Mar 25 '09 at 18:17
  • 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 Mar 25 '09 at 19:30
  • 1
    @MichaelMeadows: Where do you see anything being iterated twice? – Ry- Nov 04 '13 at 04:04
  • 1
    I did some basic testing here, and stepping on N was about 35 times faster than using this linq query, even though the latter is far nicer to look at – Jack Aug 26 '18 at 22:22
39

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

Michael Todd
  • 16,679
  • 4
  • 49
  • 69
  • That was basically my thought. – Mark Pim Mar 25 '09 at 17:31
  • 2
    @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 Mar 25 '09 at 17:38
  • 8
    @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.) – mqp Mar 25 '09 at 17:41
  • I agree go for the old school solution, and I'm even guessing this will perform better. – Jesper Fyhr Knudsen Mar 27 '09 at 12:29
  • Easy to get carried away with fancy new syntax. Its fun though. – Ronnie Nov 21 '12 at 13:57
39

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> list, 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];
Luc Morin
  • 5,302
  • 20
  • 39
MartinStettner
  • 28,719
  • 15
  • 79
  • 106
  • 2
    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 Mar 25 '09 at 17:42
  • Not sure how list work, but why you use a loop and return `list[i]` instead just return `list[n-1]`? – Juan Carlos Oropeza Sep 29 '16 at 18:28
  • @JuanCarlosOropeza he returns every nth element (e.g. 0, 3, 6...), not just the nth element of the list. – alfoks Mar 30 '17 at 08:53
  • Short of iterating through the collection manually I like this the best because you never wind up iterating past the same value more than once. – Emperor Eto Jan 28 '23 at 16:28
  • This is quite nice, but I would add an additional parameter to the extension of `int start = 0` so that this can be used multiple times with something like `.Concat()` – The Thirsty Ape May 18 '23 at 19:34
32

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

var everyFourth = list.Where((x,i) => i % 4 == 0);
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
10

For Loop

for(int i = 0; i < list.Count; i += n)
    //Nth Item..
Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132
  • Count will evaluate the enumerable. if this was done in a linq friendly manner then you could lazily eval and take the first 100 value eg `` source.TakeEvery(5).Take(100) `` If the underlying source was expensive to eval then your approach would cause every element to be evaluated – RhysC Dec 11 '15 at 06:40
  • 1
    @RhysC Good point, for enumerables in general. OTOH, Question did specify `List`, so `Count` is defined to be cheap. – ToolmakerSteve Feb 05 '19 at 18:38
6

I think if you provide a linq extension, you should be able to operate on the least specific interface, thus on IEnumerable. Of course, if you are up for speed especially for large N you might provide an overload for indexed access. The latter removes the need of iterating over large amounts of not needed data, and will be much faster than the Where clause. Providing both overloads lets the compiler select the most suitable variant.

public static class LinqExtensions
{
    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n)
    {
        if (n < 0)
            throw new ArgumentOutOfRangeException("n");
        if (n > 0)
        {
            int c = 0;
            foreach (var e in list)
            {
                if (c % n == 0)
                    yield return e;
                c++;
            }
        }
    }
    public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
    {
        if (n < 0)
            throw new ArgumentOutOfRangeException("n");
        if (n > 0)
            for (int c = 0; c < list.Count; c += n)
                yield return list[c];
    }
}
belucha
  • 91
  • 1
  • 2
4

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
  • 687,336
  • 108
  • 737
  • 1,005
1

Imho no answer is right. All solutions begins from 0. But I want to have the real nth element

public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
{
    for (int i = n - 1; i < list.Count; i += n)
        yield return list[i];
}
1

@belucha I like this, because the client code is very readable and the Compiler chooses the most efficient Implementation. I would build upon this by reducing the requirements to IReadOnlyList<T> and to save the Division for high-performance LINQ:

    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) {
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        int i = n;
        foreach (var e in list) {
            if (++i < n) { //save Division
                continue;
            }
            i = 0;
            yield return e;
        }
    }

    public static IEnumerable<T> GetNth<T>(this IReadOnlyList<T> list, int n
        , int offset = 0) { //use IReadOnlyList<T>
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        for (var i = offset; i < list.Count; i += n) {
            yield return list[i];
        }
    }
Spoc
  • 668
  • 5
  • 14
0
private static readonly string[] sequence = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15".Split(',');

static void Main(string[] args)
{
    var every4thElement = sequence
      .Where((p, index) => index % 4 == 0);

    foreach (string p in every4thElement)
    {
        Console.WriteLine("{0}", p);
    }

    Console.ReadKey();
}

output

enter image description here

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Anwar Ul-haq
  • 1,851
  • 1
  • 16
  • 28