如何从List <T>中获取第n个项目?


114

我正在使用.NET 3.5,并且希望能够n从列表中获取每个* *项目。我不介意使用lambda表达式还是LINQ实现它。

编辑

看起来这个问题引起了很多争论(这是一件好事,对吧?)。我学到的主要内容是,当您认为自己知道做某事的所有方法(甚至如此简单)时,请三思!


我没有删除您原始问题背后的任何含义;我只清理了一下,正确使用了大写和标点符号。(.NET是大写字母,LINQ是全大写字母,不是'lambda',而是'lambda表达式'。)
George Stocker

1
您用完全不是同义词的“ sure”替换了“ fussed”。
mqp

似乎是这样。确保也没有意义,除非它是“我不确定使用...是否可以实现”
塞缪尔

是的,据我了解,这是正确的。
mqp

大惊小怪的是,最好将其替换为“有关”,以便其显示为“我不关心它是使用lambda表达式还是LINQ实现的”。
TheTXI

Answers:


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

5
@mquander:请注意,这实际上将为您提供第n-1个元素。如果您想要实际的第n个元素(跳过第一个),则必须将1加到i。
casperOne

2
是的,我想这取决于您所说的“第n个”,但是您的解释可能更普遍。从i中添加或减去以适合您的需求。
mqp

5
请注意:与固定增量的简单循环相比,Linq / Lambda解决方案的性能要低得多。
MartinStettner

5
不一定,通过延迟执行,它可以在foreach循环中使用,并且只循环原始列表一次。
塞缪尔

1
这取决于您所说的“实用”。如果您需要一种快速的方法来在用户单击按钮时获取30个项目列表中的所有其他项目,那么我认为这很实际。有时性能不再重要。当然,有时候确实如此。
mqp

37

我知道这是“老派”,但为什么不只使用step = n的for循环?


那基本上是我的想法。
Mark Pim

1
@Michael Todd:它可以工作,但是问题在于您必须在各处复制该功能。通过使用LINQ,它成为组合查询的一部分。
casperOne

8
@casperOne:我相信程序员发明了这种称为子例程的东西来处理这个问题;)尽管有LINQ版本,在真实程序中我还是会使用循环,因为循环意味着您不必遍历每个元素(将索引增加N。)
mqp,2009年

我同意去老派的解决方案,甚至猜测这会更好。
Jesper Fyhr Knudsen,2009年

易于被新奇的语法所迷惑。它的乐趣。
罗尼

34

听起来好像

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

会成功的 我看不到需要使用Linq或lambda表达式。

编辑:

做了

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];
  }
}

你用LINQish的方式写

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

第二次编辑

使其更加LINQish

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

2
我喜欢使用this [] getter而不是Where()方法的方法,该方法本质上是迭代IEnumerable的每个元素。如果您具有IList / ICollection类型,这是更好的方法,恕我直言。
spoulson

不知道列表是如何工作的,但是为什么要使用循环然后返回list[i]而只是返回list[n-1]
Juan Carlos Oropeza

@JuanCarlosOropeza他返回每个第n个元素(例如0、3、6 ...),而不仅仅是返回列表的第n个元素。
alfoks


10

对于循环

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

计数将评估可枚举。如果这是以linq友好的方式完成的,那么您可以懒惰地评估并采用前100个值,例如``source.TakeEvery(5).Take(100)``如果基础来源评估成本很高,则您的方法将导致要评估的元素
RhysC 2015年

1
@RhysC好点,对于一般的可枚举对象。OTOH,Question确实指定了List<T>,因此Count定义为便宜。
ToolmakerSteve

3

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

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

这将获得第一个项目,然后从那里获得每五个项目。如果要从第五项而不是第一项开始,请与4比较而不是与0比较。


3

我认为,如果您提供linq扩展名,则应该可以在最不特定的接口上进行操作,从而可以在IEnumerable上进行操作。当然,如果您特别希望处理较大的N,则可能会增加索引访问的过载。后者消除了对大量不需要的数据进行迭代的需要,并且比Where子句快得多。提供两个重载可以使编译器选择最合适的变体。

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];
    }
}

这适用于任何列表吗?因为我尝试在列表中使用自定义类并返回IEnumarted <class>而不是<class>,并且强制覆盖(class)List.GetNth(1)都不起作用。
Juan Carlos Oropeza

是我的错,我必须包括GetNth(1).FirstOrDefault();。
Juan Carlos Oropeza

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();
}

输出

在此处输入图片说明


0

恕我直言,没有答案是正确的。所有解决方案都从0开始。但是我想拥有真正的第n个元素

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];
}

0

@belucha我喜欢这样,因为客户端代码可读性强,并且编译器选择了最有效的实现。在此基础上,我将减少对IReadOnlyList<T>高性能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];
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.