【问题标题】:Linq Select 5 items per Iteration [duplicate]Linq每次迭代选择5个项目[重复]
【发布时间】:2012-07-10 18:16:54
【问题描述】:

Linq 根据我们的枚举器每次选择 5 个项目

我们的列表,例如 "theList"100 项, 想要遍历列表并在每次迭代中选择 5 个项目

示例代码,我们希望将其更改为我们想要的结果:

        theList = dt.AsEnumerable()
            .Select(row => new CustItem
            {
                Name = row.Field<string>("tName"),
                Title = row.Field<string>("tTitle"),
            }).ToList();

我们应该在一个循环中迭代它并每次处理选定的 5 个项目,或者将它传递给我们的其他方法:

类似的东西:

for (int i=0; i < 20 ; i++)

我想在linq select 语句上使用"i" 枚举器并进行多重性来确定我们新结果集的边界。

【问题讨论】:

标签: c# linq


【解决方案1】:

以下静态扩展方法可以使将列表分解为具有特定批量大小的多个列表的简单过程。

    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int noOfItemsPerBatch)
    {
        decimal iterations = items.Count() / noOfItemsPerBatch;

        var roundedIterations = (int)Math.Ceiling(iterations);

        var batches = new List<IEnumerable<T>>();

        for (int i = 0; i < roundedIterations; i++)
        {
            var newBatch = items.Skip(i * noOfItemsPerBatch).Take(noOfItemsPerBatch).ToArray();
            batches.Add(newBatch);
        }

        return batches;
    }

使用示例

var items = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var batchedItems = items.Batch(4);

Assert.AreEqual(batchedItems.Count() == 3);
Assert.AreEqual(batchedItems[0].Count() == 4);
Assert.AreEqual(batchedItems[1].Count() == 4);
Assert.AreEqual(batchedItems[2].Count() == 2);

【讨论】:

    【解决方案2】:

    您也可以通过利用整数算术和GroupBy 方法使用纯 linq 来做到这一点:

    int blockSize = 5;
    var group = theList.Select((x, index) => new { x, index })
                       .GroupBy(x => x.index / blockSize, y => y.x);
    
    foreach (var block in group)
    {
        // "block" will be an instance of IEnumerable<T>
        ...
    }
    

    这种方法有很多优点,但不一定立即显现出来:

    • 延迟执行,因此在处理条件处理时效率很高
    • 您不需要知道集合的长度,从而避免了多次枚举,同时通常也比其他方法更简洁

    【讨论】:

    • 谢谢它也可以提供帮助,+1,你能告诉一下 x,y,blocksize 和 index 吗?索引似乎是集合的起点,其他的呢?这里还有一个问题是我们需要知道我们选择的 Enumerable List 中项目的索引,这里应该是“组”。这种方式我还没试过。
    • 我使用的Select 的重载采用2 参数Func,其中参数是项目(与普通Select 一样)及其索引。然后代码将其投影到一个新的数据类型中,该数据类型包含原始项目及其索引。 GroupBy 很简单——因为整数除法总是向下取整,index/blocksize 对块中的每个项目都是相同的(即 '0/5 == 1/5 == 2/5 == 3/5 == 4/5')。我的最后一步,y =&gt; y.x 只是再次提取原始项目,但如果您想保留索引,请不要包含最后的 lambda。
    • 我找到的最佳答案!谢谢!
    【解决方案3】:

    试试这个:

     for (int i = 0; i < list.Count; i = i + 5) {
                    var fiveitems = list.Skip(i).Take(5);
                    foreach(var item in fiveitems)
                    {
    
                    }
                }
    

    【讨论】:

    • 这对 Govind 的回答有何改进?
    • 这个考虑了数组的大小
    【解决方案4】:

    听起来你想要来自MoreLINQBatch 运算符:

    foreach (var batch in query.Batch(5))
    {
        foreach (var item in batch)
        {
            ...
        } 
    }
    

    【讨论】:

    • 嗨乔恩,看起来很有趣,但需要订购,是吗?
    • 嗨,乔恩,你能告诉我 Govind 的方法有什么问题吗?
    • @Asif:嗯,这将多次迭代源,这可能并不理想。如果这真的是List 没关系,但如果它是您不想 想要放入内存的东西(例如,您正在从文本文件中流式传输行),您不想开始为每个批次再次读取文件。
    • 你可以使用 Jon 的“SmartEnumerable”扩展:msmvps.com/blogs/jon_skeet/archive/2007/07/27/…
    • @Sypress:不,目前没有“批号”指示符,尽管正如 Matthew 所说,如果你愿意,可以使用 SmartEnumerable。
    【解决方案5】:
    for (int i=0; i < 20 ; i++)
    {
        var fiveitems = theList.Skip(i*5).Take(5);
    }
    

    【讨论】:

    • 恕我直言,这对大型列表不利,因为它看起来是二次方。
    • i 不应该以 5 为单位递增吗?目前它会做 5 个批次,但相同的项目会在 4 个不同的批次中出现 4 次?
    猜你喜欢
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多