Is there any way I can separate a List<SomeObject>
into several separate lists of SomeObject
, using the item index as the delimiter of each split?
Let me exemplify:
I have a List<SomeObject>
and I need a List<List<SomeObject>>
or List<SomeObject>[]
, so that each of these resulting lists will contain a group of 3 items of the original list (sequentially).
eg.:
Original List:
[a, g, e, w, p, s, q, f, x, y, i, m, c]
Resulting lists:
[a, g, e], [w, p, s], [q, f, x], [y, i, m], [c]
I'd also need the resulting lists size to be a parameter of this function.
This question is a bit old, but I just wrote this, and I think it's a little more elegant than the other proposed solutions:
This following solution is the most compact I could come up with that is O(n).
We found David B's solution worked the best. But we adapted it to a more general solution:
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.
To insert my two cents...
By using the list type for the source to be chunked, I found another very compact solution:
You could use a number of queries that use
Take
andSkip
, but that would add too many iterations on the original list, I believe.Rather, I think you should create an iterator of your own, like so:
You can then call this and it is LINQ enabled so you can perform other operations on the resulting sequences.
In light of Sam's answer, I felt there was an easier way to do this without:
That said, here's another pass, which I've codified in an extension method to
IEnumerable<T>
calledChunk
:Nothing surprising up there, just basic error checking.
Moving on to
ChunkInternal
:Basically, it gets the
IEnumerator<T>
and manually iterates through each item. It checks to see if there any items currently to be enumerated. After each chunk is enumerated through, if there aren't any items left, it breaks out.Once it detects there are items in the sequence, it delegates the responsibility for the inner
IEnumerable<T>
implementation toChunkSequence
:Since
MoveNext
was already called on theIEnumerator<T>
passed toChunkSequence
, it yields the item returned byCurrent
and then increments the count, making sure never to return more thanchunkSize
items and moving to the next item in the sequence after every iteration (but short-circuited if the number of items yielded exceeds the chunk size).If there are no items left, then the
InternalChunk
method will make another pass in the outer loop, but whenMoveNext
is called a second time, it will still return false, as per the documentation (emphasis mine):At this point, the loop will break, and the sequence of sequences will terminate.
This is a simple test:
Output:
An important note, this will not work if you don't drain the entire child sequence or break at any point in the parent sequence. This is an important caveat, but if your use case is that you will consume every element of the sequence of sequences, then this will work for you.
Additionally, it will do strange things if you play with the order, just as Sam's did at one point.