use Scalar::MoreUtils qw(zip); # I'm lazy sub sum_group_by { my ($key_columns, $aggregate_columns, $rows) = @_; my %result; for my $input_row (@$rows) { # Create the synthetic key, assuming that no value will contain a \0: my @key_values = @{ $input_row }{ @$key_columns }; my $key = join "\0", @key_values; # If this is a new key, create an new result row for it that contains the key values: $result{ $key } ||= { zip @$key_columns, @key_values }; for my $aggregate (@$aggregate_columns) { $result{ $key }->{ $aggregate } += $input_row->{ $aggregate }; }; }; # Return the result as new rows, ordered asciibetically (because I'm lazy) by their key: map { $result{ $_ } } sort keys %result; };