关于c#:如何从List< T>中获取每个第n项?

How can I get every nth item from a List<T>?

我正在使用.NET 3.5,希望能够从列表中获取每个*n*项。我不关心它是使用lambda表达式还是使用linq实现的。

编辑

看起来这个问题引起了很多争论(这是好事,对吧?)我学到的最主要的一点是,当你认为你知道做某件事的所有方法(即使如此简单)时,再想想!


1
return list.Where((x, i) => i % nStep == 0);


我知道这是"老派",但为什么不使用一个for循环,stepping=n?


听起来像

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

会成功的。我不认为需要使用LINQ或lambda表达式。

编辑:

成功

1
2
3
4
5
6
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];
  }
}

你的写作方式很简单

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

第二次编辑:

使之更具灵气

1
from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];


可以使用Where重载,该重载与元素一起传递索引。

1
var everyFourth = list.Where((x,i) => i % 4 == 0);


for循环

1
2
for(int i = 0; i < list.Count; i += n)
    //Nth Item..


我认为如果您提供一个LINQ扩展,您应该能够在最不特定的接口上操作,从而在IEnumerable上操作。当然,如果您提高了速度,特别是对于大型N,那么可能会为索引访问提供过载。后者消除了对大量不需要的数据进行迭代的需要,并且比WHERE子句快得多。提供这两个重载可以让编译器选择最合适的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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];
    }
}


我不确定是否可以使用LINQ表达式,但我知道您可以使用Where扩展方法来实现它。例如,要获取每五个项目:

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

这将得到第一个项目和每五个从那里。如果你想从第五项开始而不是从第一项开始,你可以用4来比较,而不是用0来比较。


@Belucha我喜欢这样,因为客户机代码可读性很好,编译器选择了最有效的实现。在此基础上,我将减少对IReadOnlyList的要求,并将部门保留为高性能LINQ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    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];
        }
    }


我不知道答案是对的。所有解决方案都从0开始。但是我想要真正的n个元素

1
2
3
4
5
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
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}

输出

enter image description here