I wrote a small perl module that dealing with PHP-ish array structures, and I'm going to release it to CPAN.

I want it to be reviewed before the release about the module naming and the code itself.

Could you please give me improvements? Any suggestions are appreciated.


use strict;
use warnings;
package Object::Squash;
# ABSTRACT: Remove numbered keys from a nested object

use parent 'Exporter';
use List::Util qw/max/;

use version; our $VERSION = version->declare("v0.0.1");

our @EXPORT_OK = qw(squash);

sub squash {
    my $obj = shift;
    return $obj unless ref $obj;

    $obj = _squash_hash($obj);
    $obj = _squash_array($obj);

    return $obj;

sub _squash_hash {
    my $obj = shift;
    return $obj unless ref $obj eq 'HASH';

    my @keys = keys %{$obj};

    if (grep {/\D/} @keys) {
        return +{
            map { $_ => squash($obj->{$_}) } @keys,

    my $max = max(@keys) || 0;

    my @ar;
    for my $i (0 .. $max) {
        push @ar, sub {
            return (undef) unless exists $obj->{$i};
            return squash($obj->{$i});

    return \@ar;

sub _squash_array {
    my $obj = shift;
    return $obj unless ref $obj eq 'ARRAY';

    return (undef) if @{$obj} == 0;
    $obj = squash($obj->[0]) if @{$obj} == 1;

    return $obj;


=head1 NAME

Object::Squash - Remove numbered keys from a nested object


This package provides B<squash> subroutine to simplify hash/array structures.

I sometimes want to walk through a data structure that consists only of a bunch
of nested hashes, even if some of them should be treated as arrays or single
values.  This module removes numbered keys from a hash.


=head2 C<squash>

    use Object::Squash qw(squash);
    my $hash = squash(+{
        foo => +{
            '0' => 'nested',
            '1' => 'numbered',
            '2' => 'hash',
            '3' => 'structures',
        bar => +{
            '0' => 'obviously a single value',

$hash now turns to:

        foo => [
        bar => 'obviously a single value',

=head1 AUTHOR

Shin Kojima <[email protected]>

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
  • \$\begingroup\$ You can try PrePAN. \$\endgroup\$
    – choroba
    Aug 24, 2014 at 17:11

1 Answer 1


Just a few remarks about parts which poke the eye,

    push @ar, sub {
        return (undef) unless exists $obj->{$i};
        return squash($obj->{$i});

introduces unnecessary subroutine call/overhead which can be replaced with do {} block, or even simpler,

    push @ar, exists $obj->{$i} ? squash($obj->{$i}) : undef;

This one is only matter of taste, so

return $obj unless ref $obj eq 'HASH';

could also be written as

return $obj if ref $obj ne 'HASH';

Usually you don't have to explicitly return undef, as in list context you may end up with list one element long, and that might not be what is intended. Instead just return; or return (); will produce empty list, or undef in scalar context. So

return (undef) if @{$obj} == 0;

could be better as

return () if @{$obj} == 0;

or perhaps,

@$obj or return;
  • 1
    \$\begingroup\$ Thank you for you reply! Actually the subroutine call in _squash_hash is unnecessary. About return (undef) I did it on the purpose, since some numbered keys could be partially discreated. I also need them as undef. \$\endgroup\$
    – ernix
    Aug 24, 2014 at 17:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.