=pod =head1 buckets buckets (size=> N, els=> \@ARRAY ,opt[ ref_to=>\&CODE ] ); Given a size and an array reference, splits the array into an AoA with each element being a reference to an array whose elements are less than C in total. If an element is larger than C it is placed into a single "bucket". If the elements of the original array are more complex then numeric scalars, you can pass a code ref that returns access to the criteria element. Example: my @array = (10 ,9 , 15, 40, 67, 2, 35); my @res = buckets(size=>30, els=>\@array); \@res = [ [ 50 ], [ 67 ], [ 40 ], [ 15, 10 ], [ 9, 2 ] ]; Or with references.. my @array = ( ['foo',10 ], ['bar', 4 ], ['base', 18 ], ['foobar',5 ], ) ; my @res = buckets(size=>30, els=>\@array, ref_to=>sub { $_->[1] } ); It does not attempt to find the optimal set of elements. It simply sorts the values in reverse numeric order. =cut sub buckets { my %args = @_; die "missing size argument: " unless defined $args{size}; die "argument element is not an ARRAY reference: $args{els}" unless UNIVERSAL::isa($args{els},'ARRAY'); die "argument ref_to is not a CODE reference: $args{ref_to}" if defined $args{ref_to} and !UNIVERSAL::isa($args{ref_to},'CODE'); return () unless @{$args{els}}; my @orig = @{$args{els}}; my @els = defined $args{ref_to} ? sort { $b->[0] <=> $a->[0] } map { [ &{$args{ref_to}}, $_ ] } @orig : sort {$b <=> $a} @orig; my ($size,@res) = (0); local $_; { $_ = shift @els; $_ = $_->[1] if $args{ref_to}; my $el = $args{ref_to} ? &{$args{ref_to}} : $_ ; $size = push(@res,[]) && 0 if $size + $el > $args{size} or $el > $args{size} ; $size+=$el; push @{$res[-1]}, $_; redo if @els; } @res; }