24

Possible Duplicate:
How can I split an IEnumerable<String> into groups of IEnumerable<string>

I have a list that I would like to break into groups of 10.

If I have an object

List<Person> allPendingPersons 

that is of length m.

Is there an elegant way in LINQ to break up allPendingPersons into one or more List objects that all have a up to 10 Persons?

Community
  • 1
  • 1
Diskdrive
  • 18,107
  • 27
  • 101
  • 167

7 Answers7

52

You can write your own extension method:

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

I explored this in more depth in my blog.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
18
 var groups = allPendingPersons.Select((p, index) => new {p,index})
                               .GroupBy(a =>a.index/10 );

if you want to process IGrouping<,>. If you are looking for List> back you could try

var listOfLists = allPendingPersons.Select((p, index) => new {p, index})
    .GroupBy(a => a.index/10)
    .Select((grp => grp.Select(g => g.p).ToList()))
    .ToList();
Bala R
  • 107,317
  • 23
  • 199
  • 210
  • not the most efficient, GroupBy will buffer all elements and build the lookup. Thus, memory+cpu overhead + not a steaming solution – Mikl X Apr 22 '15 at 23:52
  • Moreover, this will create n lists of 1 element each. Not 10. – CTZStef Oct 29 '18 at 14:20
7

The Reactive Extensions for .NET (Rx) has an extension method that does exactly what you want:

var buffered = allPendingPersons.BufferWithCount(10);

If you want to do it using LINQ you could do this:

var buffered =
    allPendingPersons
        .Select((p, i) => new { Group = i / 10, Person = p })
        .GroupBy(x => x.Group, x => x.Person)
        .Select(g => g.ToArray());
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
4

These should be of some assistance.

How can I split an IEnumerable<String> into groups of IEnumerable<string>
Divide a large IEnumerable into smaller IEnumerable of a fix amount of item
Can I improve these Pagination extension methods?
Get groups of 4 elements from name value list using LINQ in C#
LINQ: Get min and max values of sequence of numbers divided into subsequences
LINQ Partition List into Lists of 8 members

One very popular answer is to check out Jon Skeet's MoreLinq, in particular, the Batch function, which not only does what you are asking for, but also lets you specify a return selector!

Community
  • 1
  • 1
diceguyd30
  • 2,742
  • 20
  • 18
  • +1 for MoreLink's `Batch`, [see source here](https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/Batch.cs) – lagerone Sep 06 '16 at 08:26
2

It's not the most efficient technique, but this will produce an IEnumerable<IEnumerable<Person>> sequence, with each inner sequence containing ten elements:

var query = allPendingPersons.Select((x, i) => new { Value = x, Group = i / 10 })
                             .GroupBy(x => x.Group,
                                      (k, g) => g.Select(x => x.Value));

And if the result really does need to be a list-of-lists rather than a simple sequence then you can create a List<List<Person>> instead by adding in a couple of ToList calls:

var query = allPendingPersons.Select((x, i) => new { Value = x, Group = i / 10 })
                             .GroupBy(x => x.Group,
                                      (k, g) => g.Select(x => x.Value).ToList())
                             .ToList();
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • 1
    You can simplify your first version a little bit by writing `x => x.Value` instead of `(k, g) => g.Select(x => x.Value)`. – Elian Ebbing Mar 07 '11 at 03:40
0

Try an iterator block:

public static IEnumerable<List<Person>> AsGroups(this List<Person> persons)
{
    var buf = new List<Person>(10);
    for (int i = 0; i<persons.Count i++;)
    {
        buf.Add(persons[i]);
        if (i%10 == 0 && buf.Count > 0)
        {
           yield return buf;
           buf = new List<Person>(10);
        }
    }
    yield return buf;
 }
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
0

Is there an elegant way in LINQ

The elegant way is not very performant. Here is a more performant way...

    public static List<List<T>> Chunk<T>(
      this List<T> theList,
      int chunkSize
    )
    {
        if (!theList.Any())
        {
            return new List<List<T>>();
        }

        List<List<T>> result = new List<List<T>>();
        List<T> currentList = new List<T>();
        result.Add(currentList);

        int i = 0;
        foreach(T item in theList)
        {
            if (i >= chunkSize)
            {
                i = 0;
                currentList = new List<T>();
                result.Add(currentList);
            }
            i += 1;
            currentList.Add(item);
        }
        return result;
    }
Community
  • 1
  • 1
Amy B
  • 108,202
  • 21
  • 135
  • 185