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?

0

7 Answers 7

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.

Sign up to request clarification or add additional context in comments.

4 Comments

slightly better to do: partition = new List<T>(size);
@nawfal: You're right; fixed.
this is so good! I used it directly on my list of items that needed action on. foreach (var b in senders.Partition(threshold)) { handleBatch(b); } simple and elegant, thanks!
Starred this Q because of your smart A. ;)
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(); 

2 Comments

not the most efficient, GroupBy will buffer all elements and build the lookup. Thus, memory+cpu overhead + not a steaming solution
Moreover, this will create n lists of 1 element each. Not 10.
8

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()); 

Comments

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!

1 Comment

+1 for MoreLink's Batch, see source here
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(); 

1 Comment

You can simplify your first version a little bit by writing x => x.Value instead of (k, g) => g.Select(x => x.Value).
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; } 

Comments

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; } 

1 Comment

You can be more performant than that by writing an iterator. See my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.