Split List into Sublists with LINQ

前端 未结 30 2440
灰色年华
灰色年华 2020-11-21 06:26

Is there any way I can separate a List into several separate lists of SomeObject, using the item index as the delimiter of each s

30条回答
  •  悲哀的现实
    2020-11-21 07:01

    Ok, here's my take on it:

    • completely lazy: works on infinite enumerables
    • no intermediate copying/buffering
    • O(n) execution time
    • works also when inner sequences are only partially consumed

    public static IEnumerable> Chunks(this IEnumerable enumerable,
                                                        int chunkSize)
    {
        if (chunkSize < 1) throw new ArgumentException("chunkSize must be positive");
    
        using (var e = enumerable.GetEnumerator())
        while (e.MoveNext())
        {
            var remaining = chunkSize;    // elements remaining in the current chunk
            var innerMoveNext = new Func(() => --remaining > 0 && e.MoveNext());
    
            yield return e.GetChunk(innerMoveNext);
            while (innerMoveNext()) {/* discard elements skipped by inner iterator */}
        }
    }
    
    private static IEnumerable GetChunk(this IEnumerator e,
                                              Func innerMoveNext)
    {
        do yield return e.Current;
        while (innerMoveNext());
    }
    

    Example Usage

    var src = new [] {1, 2, 3, 4, 5, 6}; 
    
    var c3 = src.Chunks(3);      // {{1, 2, 3}, {4, 5, 6}}; 
    var c4 = src.Chunks(4);      // {{1, 2, 3, 4}, {5, 6}}; 
    
    var sum   = c3.Select(c => c.Sum());    // {6, 15}
    var count = c3.Count();                 // 2
    var take2 = c3.Select(c => c.Take(2));  // {{1, 2}, {4, 5}}
    

    Explanations

    The code works by nesting two yield based iterators.

    The outer iterator must keep track of how many elements have been effectively consumed by the inner (chunk) iterator. This is done by closing over remaining with innerMoveNext(). Unconsumed elements of a chunk are discarded before the next chunk is yielded by the outer iterator. This is necessary because otherwise you get inconsistent results, when the inner enumerables are not (completely) consumed (e.g. c3.Count() would return 6).

    Note: The answer has been updated to address the shortcomings pointed out by @aolszowka.

提交回复
热议问题