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

Posted

技术标签:

【中文标题】如何从 List<T> 中获取每个第 n 个项目?【英文标题】:How can I get every nth item from a List<T>? 【发布时间】:2009-03-25 17:27:55 【问题描述】:

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

编辑

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

【问题讨论】:

我没有删掉你原来问题背后的任何含义;我只清理了它并正确使用了大写和标点符号。 (.NET 是大写的,LINQ 是全大写的,它不是“lambda”,而是一个“lambda 表达式”。) 您将“fussed”替换为“sure”,而这根本不是同义词。 似乎是这样。确定也没有意义,除非它是“我不确定是否可以使用......” 是的,据我了解,差不多。 fussed 可能最好替换为“关注”,这样它就会显示为“我不关心它是使用 lambda 表达式还是 LINQ 实现的。” 【参考方案1】:
return list.Where((x, i) => i % nStep == 0);

【讨论】:

@mquander:请注意,这实际上会给您第 n - 1 个元素。如果您想要实际的第 n 个元素(跳过第一个元素),那么您必须将 1 添加到 i。 是的,我想这有点取决于您所说的“nth”是什么意思,但您的解释可能更常见。从 i 中添加或减去以满足您的需要。 请注意:Linq/Lambda 解决方案的性能将远低于具有固定增量的简单循环。 不一定,通过延迟执行,它可以用于 foreach 循环,并且只在原始列表上循环一次。 这取决于您所说的“实用”。如果您需要一种在用户单击按钮时快速获取 30 个项目列表中的所有其他项目的方法,我会说这非常实用。有时性能真的不再重要了。当然,有时确实如此。【参考方案2】:

我知道这是“老派”,但为什么不直接使用带有 stepping = n 的 for 循环呢?

【讨论】:

这基本上是我的想法。 @Michael Todd:它有效,但问题是您必须在任何地方复制该功能。通过使用 LINQ,它成为组合查询的一部分。 @casperOne:我相信程序员发明了这个叫做子例程的东西来处理这个问题;)在一个真正的程序中,我可能会使用循环,尽管有聪明的 LINQ 版本,因为循环意味着你不需要'必须遍历每个元素(将索引增加 N。) 我同意使用老派的解决方案,我什至猜测这会表现得更好。 很容易被花哨的新语法迷住。不过很有趣。【参考方案3】:

听起来像

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

【讨论】:

我喜欢这种使用 this[] getter 而不是 Where() 方法的方法,它本质上是迭代 IEnumerable 的每个元素。如果您有 IList/ICollection 类型,这是更好的方法,恕我直言。 不确定列表是如何工作的,但为什么你使用循环并返回 list[i] 而只是返回 list[n-1] @JuanCarlosOropeza 他返回每个第 n 个元素(例如 0、3、6...),而不仅仅是列表的第 n 个元素。【参考方案4】:

您可以使用 Where 重载将索引与元素一起传递

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

【讨论】:

得说我是这种方法的粉丝。 我一直忘记你可以做到这一点 - 非常好。【参考方案5】:

for循环

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

【讨论】:

Count 将评估可枚举。如果这是以 linq 友好的方式完成的,那么您可以懒惰地评估并获取前 100 个值,例如 ``source.TakeEvery(5).Take(100) `` 如果基础源的评估成本很高,那么您的方法将导致每个要评估的元素 @RhysC 好点,对于一般的可枚举。 OTOH,问题确实指定了List&lt;T&gt;,所以Count 被定义为便宜。【参考方案6】:

我认为如果你提供一个 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];
    

【讨论】:

这适用于任何列表吗?因为我尝试在 List 中使用自定义类并返回 IEnumarted 而不是 并且强制覆盖 (class)List.GetNth(1) 也不起作用。 是我的错,我必须包含 GetNth(1).FirstOrDefault();【参考方案7】:

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

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

这将获得第一个项目,然后每五分之一。如果要从第 5 项而不是第 1 项开始,则与 4 进行比较,而不是与 0 进行比较。

【讨论】:

【参考方案8】:

恕我直言,没有答案是正确的。所有解决方案都从 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];

【讨论】:

【参考方案9】:

@belucha 我喜欢这个,因为客户端代码可读性很强,编译器会选择最高效的实现。我会以此为基础,将要求降低到 IReadOnlyList&lt;T&gt;,并为高性能 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];
        
    

【讨论】:

【参考方案10】:
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();

输出

【讨论】:

以上是关于如何从 List<T> 中获取每个第 n 个项目?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 C# 中的通用 List<T> 中获取元素? [复制]

如何连接 IQueryable<T> 和 List<T>

如何使用 Linq 从 List<Object> 中获取第一个对象

如何反射获取List<T> 中泛型的类型

java中怎么取两个数组中不相同的数据?

如何从 simplejdbctemplate 查询中获取字符串列表或类型 T?