Grouping lists into groups of X items per group

后端 未结 5 758
一个人的身影
一个人的身影 2020-12-09 22:54

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

5条回答
  •  孤城傲影
    2020-12-09 23:50

    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.

    • IF producing a group is an O(n/2) operation on the average, and
    • Given a group size of s, the production of approximately n/s groups is required,

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

提交回复
热议问题