Split List into Sublists with LINQ

前端 未结 30 2386
灰色年华
灰色年华 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 06:59

    For anyone interested in a packaged/maintained solution, the MoreLINQ library provides the Batch extension method which matches your requested behavior:

    IEnumerable<char> source = "Example string";
    IEnumerable<IEnumerable<char>> chunksOfThreeChars = source.Batch(3);
    

    The Batch implementation is similar to Cameron MacFarland's answer, with the addition of an overload for transforming the chunk/batch before returning, and performs quite well.

    0 讨论(0)
  • 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<IEnumerable<T>> Chunks<T>(this IEnumerable<T> 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<bool>(() => --remaining > 0 && e.MoveNext());
    
            yield return e.GetChunk(innerMoveNext);
            while (innerMoveNext()) {/* discard elements skipped by inner iterator */}
        }
    }
    
    private static IEnumerable<T> GetChunk<T>(this IEnumerator<T> e,
                                              Func<bool> 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.

    0 讨论(0)
  • 2020-11-21 07:01

    Here's a list splitting routine I wrote a couple months ago:

    public static List<List<T>> Chunk<T>(
        List<T> theList,
        int chunkSize
    )
    {
        List<List<T>> result = theList
            .Select((x, i) => new {
                data = x,
                indexgroup = i / chunkSize
            })
            .GroupBy(x => x.indexgroup, x => x.data)
            .Select(g => new List<T>(g))
            .ToList();
    
        return result;
    }
    
    0 讨论(0)
  • 2020-11-21 07:01

    I find this little snippet does the job quite nicely.

    public static IEnumerable<List<T>> Chunked<T>(this List<T> source, int chunkSize)
    {
        var offset = 0;
    
        while (offset < source.Count)
        {
            yield return source.GetRange(offset, Math.Min(source.Count - offset, chunkSize));
            offset += chunkSize;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 07:03

    I think the following suggestion would be the fastest. I am sacrificing the lazyness of the source Enumerable for the ability to use Array.Copy and knowing ahead of the time the length of each of my sublists.

    public static IEnumerable<T[]> Chunk<T>(this IEnumerable<T> items, int size)
    {
        T[] array = items as T[] ?? items.ToArray();
        for (int i = 0; i < array.Length; i+=size)
        {
            T[] chunk = new T[Math.Min(size, array.Length - i)];
            Array.Copy(array, i, chunk, 0, chunk.Length);
            yield return chunk;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 07:03
    public static List<List<T>> GetSplitItemsList<T>(List<T> originalItemsList, short number)
        {
            var listGroup = new List<List<T>>();
            int j = number;
            for (int i = 0; i < originalItemsList.Count; i += number)
            {
                var cList = originalItemsList.Take(j).Skip(i).ToList();
                j += number;
                listGroup.Add(cList);
            }
            return listGroup;
        }
    
    0 讨论(0)
提交回复
热议问题