File Coverage

blib/lib/Data/HashArray.pm
Criterion Covered Total %
statement 56 69 81.1
branch 10 20 50.0
condition 1 6 16.6
subroutine 13 15 86.6
pod 2 2 100.0
total 82 112 73.2


line stmt bran cond sub pod time code
1             # $Id$
2             #
3 2     2   34015 use strict;
  2         4  
  2         160  
4 2     2   12 use warnings;
  2         3  
  2         74  
5 2     2   21 no warnings qw(uninitialized);
  2         4  
  2         82  
6 2     2   1189 use utf8;
  2         14  
  2         13  
7              
8             package Data::HashArray;
9             require 5.008;
10              
11 2     2   106 use Carp qw(carp cluck);
  2         4  
  2         174  
12 2     2   2393 use Data::Dumper;
  2         22067  
  2         159  
13 2     2   18 use Scalar::Util qw(reftype);
  2         4  
  2         199  
14              
15 2     2   12 use vars qw($VERSION);
  2         4  
  2         263  
16             $VERSION = '1.03';
17              
18             our $AUTOLOAD;
19              
20             # Hash deref. gives access to the first element
21             # For debugging purpose only
22             #carp "\033\[0;35m[NOTICE] Overloaded '\"\"' called (".
23             # __PACKAGE__.")\033\[0m";
24             use overload (
25 28     28   4479 '%{}' => sub { $_[0]->_hash_access; },
26 0     0   0 '""' => sub { "". $_[0]->_hash_access; },
27 2         24 'fallback' => 1,
28 2     2   11 );
  2         5  
29              
30             # --------------------------------------------------------------------
31             # Constructor
32             # --------------------------------------------------------------------
33              
34             sub new {
35 12     12 1 1000 my ($proto, @array) = @_;
36 12   33     47 my $class = ref($proto) || $proto;
37              
38 12         53 bless \@array, $class;
39             }
40              
41              
42             # Return the first element (which is normally a hash ref) of the list
43             sub _hash_access () {
44 28     28   47 my ($self) = @_;
45              
46 28         45 my $item = eval { $self->[0]; };
  28         59  
47 28 50       62 if ($@) {
48 0         0 cluck "$@";
49             }
50              
51             #carp "Hash-access => DONE\n";
52 28         157 return $item;
53             }
54              
55             #-------------------------------------------------------
56             # hash(field1, field2, field3, ...)
57             #
58             # Return a hash keyed on field1. If there is no field2, this will be
59             # a hash of HashArrays. If field2 exists, this will be a hash of hashes of
60             # HashArrays. And so on...
61             #
62             # Note that field1; field2, ... may be CODE references, too. In that case, the sub gets called
63             # at least once for each item in the array. The item is passed as an argument to the sub.
64             #
65             # Breadth-first recursive.
66             #-------------------------------------------------------
67             sub hash {
68 5     5 1 1001 my $self = shift;
69 5         10 my $class = ref($self);
70 5 50       17 return undef unless (@_);
71 5         8 my $field = shift;
72 5 50       12 return undef unless defined($field);
73            
74 5         9 my $h = {};
75            
76             # Hash the array on '$field';
77 5         12 foreach my $item (@$self) {
78 12         15 my $key;
79            
80 12 100       39 if (ref($field) eq 'CODE') {
    50          
81             # Field is a CODE refernce. Call it, with 'item' passed as an argument
82 3         8 $key = &$field($item);
83             } elsif (UNIVERSAL::can($item, $field)) {
84             # Field has an accessor. Call it (the resukt should stringify to a hash key).
85 0         0 $key = $item->$field();
86             } else {
87             # Field should otherwise stringify to a hash key
88 9         17 $key = $item->{$field};
89             }
90            
91             # If the keyed item doesn't yet exist, create a new NodeArray and assign it.
92 12 100       37 unless ( exists($h->{$key}) ) {
93 11         29 $h->{$key} = $class->new();
94             }
95            
96             # Push the item on the keyed NodeArray.
97 12         21 my $array= $h->{$key};
98 12         31 push @$array, $item;
99             }
100              
101             # If we don't have any more fields, just return the hash.
102 5 100       20 return $h unless (@_);
103              
104             # Otherwise, further hash each item in the hash on the remaining fields.
105 1         4 foreach my $key (keys %$h) {
106 2         5 my $array = $h->{$key};
107 2         9 $h->{$key} = $array->hash(@_);
108             }
109 1         4 return $h;
110             }
111              
112              
113             #-------------------------------------------------------
114             # By default, all method calls are delegated to the first element.
115             #-------------------------------------------------------
116             sub AUTOLOAD {
117 0     0     my $self = shift;
118 0           my $func = $AUTOLOAD;
119 0           $func =~ s/.*:://;
120              
121 0 0         if ($func =~ /^[0-9]+$/o) {
122 0           return eval { $self->[$func]; };
  0            
123             }
124              
125 0 0         return undef if $func eq 'DESTROY';
126 0 0 0       if (reftype($self) && reftype($self) eq 'ARRAY') {
127 0           $self->_hash_access->$func(@_);
128             } else {
129 0           cluck "*** \$self->$func";
130             }
131             }
132              
133              
134             1;
135              
136              
137             __END__