File Coverage

blib/lib/Hash/Squash.pm
Criterion Covered Total %
statement 60 60 100.0
branch 25 26 96.1
condition 2 2 100.0
subroutine 10 10 100.0
pod 2 2 100.0
total 99 100 99.0


line stmt bran cond sub pod time code
1 1     1   663 use strict;
  1         2  
  1         28  
2 1     1   5 use warnings;
  1         1  
  1         40  
3             package Hash::Squash;
4             # ABSTRACT: Remove numbered keys from a nested hash/array
5              
6 1     1   721 use parent 'Exporter';
  1         254  
  1         5  
7 1     1   43 use List::Util qw/max/;
  1         1  
  1         92  
8              
9 1     1   840 use version; our $VERSION = version->declare("v0.0.4");
  1         2059  
  1         7  
10              
11             our @EXPORT_OK = qw(squash unnumber);
12              
13 2     2 1 4421 sub squash { _squash(shift) }
14 1     1 1 2382 sub unnumber { _squash(shift, { keep_empty => 1 }) }
15              
16             sub _squash {
17 124     124   133 my ($obj, $arg) = @_;
18 124 100       265 return $obj unless ref $obj;
19              
20 31         62 $obj = _squash_hash($obj, $arg);
21 31         53 $obj = _squash_array($obj, $arg);
22              
23 31         99 return $obj;
24             }
25              
26             sub _squash_hash {
27 31     31   29 my ($obj, $arg) = @_;
28 31 100       59 return $obj if ref $obj ne 'HASH';
29              
30 24         48 EMPTY_HASH: {
31 24 100       22 last EMPTY_HASH if %{$obj};
  24         30  
32 2 100       7 return (undef) unless exists $arg->{keep_empty};
33 1 50       3 return (undef) unless $arg->{keep_empty};
34 1         3 return +{};
35             }
36              
37 22         22 my @keys = keys %{$obj};
  22         61  
38              
39 69         144 CONTAINS_NON_NUMERIC_KEYS: {
40 22 100       27 last CONTAINS_NON_NUMERIC_KEYS unless grep {/\D/} @keys;
  22         22  
41 10         10 my %hash = map { $_ => _squash($obj->{$_}, $arg) } @keys;
  28         63  
42 10         26 return \%hash;
43             }
44              
45 12   100     62 my $max = max(@keys) || 0;
46              
47 12         13 my @ar;
48 12         14 for my $i (0 .. $max) {
49             #
50             # Some numbered keys might be partially discreated
51             #
52 44 100       97 push @ar, exists $obj->{$i} ? _squash($obj->{$i}, $arg) : (undef);
53             }
54              
55 12         30 return \@ar;
56             }
57              
58             sub _squash_array {
59 31     31   36 my ($obj, $arg) = @_;
60 31 100       65 return $obj if ref $obj ne 'ARRAY';
61              
62 19 100       33 my $keep_empty = exists $arg->{keep_empty} ? $arg->{keep_empty} : ();
63              
64 19         42 EMPTY_ARRAY: {
65 19 100       18 last EMPTY_ARRAY if @{$obj} != 0;
  19         17  
66 2 100       19 return (undef) unless $keep_empty;
67 1         3 return [];
68             }
69              
70             SINGLE_ELEMENT: {
71 17 100       19 last SINGLE_ELEMENT if @{$obj} != 1;
  17         11  
  17         41  
72 7 100       12 last SINGLE_ELEMENT if $keep_empty;
73 4         12 return _squash($obj->[0]);
74             }
75              
76 13         14 my @array = map { _squash($_, $arg) } @{$obj};
  48         61  
  13         17  
77              
78 13         27 return \@array;
79             }
80              
81             1;
82             __END__