Split a collection into `n` parts with LINQ?

A pure linq and the simplest solution is as shown below.

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int i = 0;
        var splits = from item in list
                     group item by i++ % parts into part
                     select part.AsEnumerable();
        return splits;
    }
}

EDIT: Okay, it looks like I misread the question. I read it as "pieces of length n" rather than "n pieces". Doh! Considering deleting answer...

(Original answer)

I don't believe there's a built-in way of partitioning, although I intend to write one in my set of additions to LINQ to Objects. Marc Gravell has an implementation here although I would probably modify it to return a read-only view:

public static IEnumerable<IEnumerable<T>> Partition<T>
    (this IEnumerable<T> source, int size)
{
    T[] array = null;
    int count = 0;
    foreach (T item in source)
    {
        if (array == null)
        {
            array = new T[size];
        }
        array[count] = item;
        count++;
        if (count == size)
        {
            yield return new ReadOnlyCollection<T>(array);
            array = null;
            count = 0;
        }
    }
    if (array != null)
    {             
        Array.Resize(ref array, count);
        yield return new ReadOnlyCollection<T>(array);
    }
}

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
            return list.Select((item, index) => new {index, item})
                       .GroupBy(x => x.index % parts)
                       .Select(x => x.Select(y => y.item));
    }
}

Ok, I'll throw my hat in the ring. The advantages of my algorithm:

  1. No expensive multiplication, division, or modulus operators
  2. All operations are O(1) (see note below)
  3. Works for IEnumerable<> source (no Count property needed)
  4. Simple

The code:

public static IEnumerable<IEnumerable<T>>
  Section<T>(this IEnumerable<T> source, int length)
{
  if (length <= 0)
    throw new ArgumentOutOfRangeException("length");

  var section = new List<T>(length);

  foreach (var item in source)
  {
    section.Add(item);

    if (section.Count == length)
    {
      yield return section.AsReadOnly();
      section = new List<T>(length);
    }
  }

  if (section.Count > 0)
    yield return section.AsReadOnly();
}

As pointed out in the comments below, this approach doesn't actually address the original question which asked for a fixed number of sections of approximately equal length. That said, you can still use my approach to solve the original question by calling it this way:

myEnum.Section(myEnum.Count() / number_of_sections + 1)

When used in this manner, the approach is no longer O(1) as the Count() operation is O(N).


This is same as the accepted answer, but a much simpler representation:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, 
                                                   int numOfParts)
{
    int i = 0;
    return items.GroupBy(x => i++ % numOfParts);
}

The above method splits an IEnumerable<T> into N number of chunks of equal sizes or close to equal sizes.

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}

The above method splits an IEnumerable<T> into chunks of desired fixed size with total number of chunks being unimportant - which is not what the question is about.

The problem with the Split method, besides being slower, is that it scrambles the output in the sense that the grouping will be done on the basis of i'th multiple of N for each position, or in other words you don't get the chunks in the original order.

Almost every answer here either doesn't preserve order, or is about partitioning and not splitting, or is plainly wrong. Try this which is faster, preserves order but a lil' more verbose:

public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items, 
                                                   int numberOfChunks)
{
    if (numberOfChunks <= 0 || numberOfChunks > items.Count)
        throw new ArgumentOutOfRangeException("numberOfChunks");

    int sizePerPacket = items.Count / numberOfChunks;
    int extra = items.Count % numberOfChunks;

    for (int i = 0; i < numberOfChunks - extra; i++)
        yield return items.Skip(i * sizePerPacket).Take(sizePerPacket);

    int alreadyReturnedCount = (numberOfChunks - extra) * sizePerPacket;
    int toReturnCount = extra == 0 ? 0 : (items.Count - numberOfChunks) / extra + 1;
    for (int i = 0; i < extra; i++)
        yield return items.Skip(alreadyReturnedCount + i * toReturnCount).Take(toReturnCount);
}

The equivalent method for a Partition operation here


Comments

  1. Noe

    • 2018/5/15

    A pure linq and the simplest solution is as shown below. You can do: select part.AsEnumerable() instead of select (IEnumerable<T>)part. It feels more elegant.

  2. Thiago

    • 2019/6/3

    Full suite of integrated K-12 solutions designed for school and district level operations. Education resource management solutions for K-12 school systems.

  3. Jacob

    • 2019/5/8

    Is there a nice way to split a collection into n parts with LINQ? Not necessarily evenly of course. That is, I want to divide the collection into 

  4. Bernardi

    • 2017/5/27

    A pure linq and the simplest solution is as shown below. static class LinqExtensions { public static IEnumerable<IEnumerable<T>> Split<T> (this IEnumerable<T> list, int parts) { int i = 0; var splits = from item in list group item by i++ % parts into part select part.AsEnumerable (); return splits; } } Share.

  5. Johnathan

    • 2021/5/26

    Your implementation is not bad for a no-LinQ solution. But there's always room for improvement. First I'll provide a LinQ solution that provides a clean way 

  6. Tadeo

    • 2016/6/14

    Split a collection into `n` parts with LINQ? 515. How to use LINQ to select object with minimum or maximum property value. 878. When to use .First and when to use

  7. Colby

    • 2016/11/21

    Dividing by three has the effect of grouping them into groups of 3. Then convert each group to a list and the IEnumerable of List to a List of List s.

  8. Donald

    • 2021/3/23

    Split a collection into `n` parts with LINQ? (21 answers) Split a List into smaller lists of N size (21 answers)

  9. Simon

    • 2019/5/12

    static class LinqExtensions { public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts) { int i = 0; var splits = from item in 

  10. Roux

    • 2020/10/19

    I found very little - everything under a Google search for “LINQ batch” seemed to be about operations with LINQ-to-SQL. Don’t care. I found Split a collection into n parts with LINQ? on Stack Overflow but was horrified by some of the algorithms there. They all seemed to commit at least one unforgivable sin:

  11. Harold

    • 2018/7/9

    static class LinqExtensions { public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts) { int i = 0; var splits = from item in 

  12. Andrew

    • 2015/9/24

    I was recently working on an interesting problem which involved splitting a string collection into groups. We had a huge collection of email addresses stored in a string array. The task was to loop the array and select 10 emails at a time and send it for processing. Here’s some LINQ code originally shared by Eric Lippert, that can be helpful

  13. Otto

    • 2020/6/21

    Here comes another classic interview question: write a function that accepts a collection and an integer. The function must divide the 

  14. Villa

    • 2017/12/25

    We skip 1 because the first string // in the array is the student ID, not an exam score. var queryNamesScores2 = from nameLine in names let splitName = nameLine.Split(',') from scoreLine in scores let splitScoreLine = scoreLine.Split(',') where Convert.ToInt32(splitName[2]) == Convert.ToInt32(splitScoreLine[0]) select new { First = splitName[0], Last = splitName[1], ExamScores = (from scoreAsText in splitScoreLine.Skip(1) select Convert.ToInt32(scoreAsText)) .ToList() }; // Display each

  15. Harry

    • 2019/1/20

    You can use the Skip and Take Enumerable extension methods for the task. Here is the code: In C#:. public List<List<int>> splitData(int width, 

  16. Jakob

    • 2018/3/6

    7. There is not much LINQ in your code and this is a really long solution for such a simple task but let's try to change it. You want to split a sentence like this one: var sentence = " code review connects the world! share the code"; so why not use regex for this and split on every occurance of ,?!..

  17. Castiel

    • 2021/5/11

    Linq; public static partial class _30s { public static List > List nums = new Partitioning operators split the sequence (collection) into two parts and 

  18. Asher

    • 2015/2/2

    Full suite of integrated K-12 solutions designed for school and district level operations. Education resource management solutions for K-12 school systems.

  19. Colton

    • 2015/4/6

    TypeScript answers related to “linq split list into sublists based on grouping” To print the left and right parts of the pattern, use nested loops.

Comments are closed.

Recent Posts