LINQ:从集合中获取一系列元素

4

我有一组对象,需要每次处理100个对象,并在没有要处理的对象时停止工作。

是否有更好的方法使用linq来处理,而不是循环遍历每个项目并获取100个元素,然后获取下一个一百个元素等等?

谢谢


4
LukeH的答案更好,请采纳并使用它。 - Andrey
5个回答

12
static void test(IEnumerable<object> objects)
{
    while (objects.Any())
    {
        foreach (object o in objects.Take(100))
        {
        }
        objects = objects.Skip(100); 
    }
}

:)


使用此方法时请注意Entity Framework。它生成的SQL将在每次迭代中有一个额外的子查询。如果有超过50个批次,查询将失败。 - Tom Ferguson

12
int batchSize = 100;
var batched = yourCollection.Select((x, i) => new { Val = x, Idx = i })
                            .GroupBy(x => x.Idx / batchSize,
                                     (k, g) => g.Select(x => x.Val));

// and then to demonstrate...
foreach (var batch in batched)
{
    Console.WriteLine("Processing batch...");

    foreach (var item in batch)
    {
        Console.WriteLine("Processing item: " + item);
    }
}

它非常低效。GroupBy 是一项昂贵的操作。 - Andrey
@Andrey:我建议你在声称效率低下之前,先将自己的答案与“GroupBy”版本进行基准测试。你可能会对结果感到惊讶。 - LukeH
你是对的,你的方法更高效(超过10倍)! - Andrey
@Andrey:肯定有比GroupBy更高效的方法来做这件事。显而易见的技术是像OP在问题中提到的那样循环,或者可能是像Lee或Foole的答案一样。我建议使用GroupBy是因为它简洁,并且不需要任何额外的辅助代码,而不是因为它是最有效的方法。 - LukeH
是的,有更高效的代码,但我很惊讶地发现GroupBy更快。 - Andrey
刚刚遇到这个问题,想重构一些“循环直到达到一定数量,然后开始新的行”的代码。非常棒,会是一个经典的扩展方法。 - Phil Cooper

3

这将把列表分成一个包含指定数量项的子列表的列表。

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int size)
{
    int i = 0;
    List<T> list = new List<T>(size);
    foreach (T item in source)
    {
        list.Add(item);
        if (++i == size)
        {
            yield return list;
            list = new List<T>(size);
            i = 0;
        }
    }
    if (list.Count > 0)
        yield return list;
}

2

我认为linq并不适合这种处理方式 - 它主要用于对整个序列执行操作,而不是拆分或修改它们。我会通过访问底层的IEnumerator<T>来完成此操作,因为使用TakeSkip的任何方法都将非常低效。

public static void Batch<T>(this IEnumerable<T> items, int batchSize, Action<IEnumerable<T>> batchAction)
{
    if (batchSize < 1) throw new ArgumentException();

    List<T> buffer = new List<T>();
    using (var enumerator = (items ?? Enumerable.Empty<T>()).GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            buffer.Add(enumerator.Current);
            if (buffer.Count == batchSize)
            {
                batchAction(buffer);
                buffer.Clear();
            }
        }

        //execute for remaining items
        if (buffer.Count > 0)
        {
            batchAction(buffer);
        }
    }
}

0
var batchSize = 100;
for (var i = 0; i < Math.Ceiling(yourCollection.Count() / (decimal)batchSize); i++)
{
    var batch = yourCollection
        .Skip(i*batchSize)
        .Take(batchSize);

    // Do something with batch
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接