I\'m having a problem knowing the best way to make a method to group a list of items into groups of (for example) no more than 3 items. I\'ve created the method below, but w
The problem with using GroupBy() is that unless it somehow has knowledge under the hood that the input is ordered by key value, it has to read the entire sequence and allocate everything to its bucket before it can emit a single group. That's overkill in this case, since the key is a function of its ordinal position within the sequence.
I like the source.Skip(m).Take(n) approach, but that makes assumptions that items in source can be directly addressed. If that's not true or Skip() and Take() have no knowledge of the underlying implementation, then the production of each group is going to be an O(n/2) operation on the average, as it repeatedly iterates over source to produce the group.
This makes the overall partitioning operation, potentially quite expensive.
Then the total cost of the operation is something like O(n2/2s), right?
So, I would do something this, an O(n) operation (feel free to use an IGrouping implementation if you'd like):
public static IEnumerable> Partition( this IEnumerable source , int partitionSize )
{
if ( source == null ) throw new ArgumentNullException("source") ;
if ( partitionSize < 1 ) throw new ArgumentOutOfRangeException("partitionSize") ;
int i = 0 ;
List partition = new List( partitionSize ) ;
foreach( T item in source )
{
partition.Add(item) ;
if ( partition.Count == partitionSize )
{
yield return new KeyValuePair( ++i , partition.ToArray() ) ;
partition.Clear() ;
}
}
// return the last partition if necessary
if ( partition.Count > 0 )
{
yield return new Partition( ++i , items.ToArray() ) ;
}
}