File Coverage

blib/lib/Hash/Tally.pm
Criterion Covered Total %
statement 40 41 97.5
branch 5 6 83.3
condition 1 2 50.0
subroutine 6 6 100.0
pod 1 1 100.0
total 53 56 94.6


line stmt bran cond sub pod time code
1             package Hash::Tally;
2              
3 2     2   51142 use strict;
  2         4  
  2         73  
4 2     2   11 use warnings;
  2         3  
  2         59  
5 2     2   11 use base qw( Exporter );
  2         7  
  2         208  
6 2     2   10 use List::Util;
  2         4  
  2         205  
7 2     2   1830 use List::MoreUtils qw( uniq );
  2         5623  
  2         904  
8              
9             our $VERSION = '0.02';
10              
11             our @EXPORT_OK = qw( tally );
12              
13             =head1 NAME
14              
15             Hash::Tally - Compute the tallies of hash values
16              
17             =head1 SYNOPSIS
18            
19            
20             use Hash::Tally qw( tally );
21            
22             my $data = {
23             Shipping => {
24             English => {
25             Canada => 8,
26             'United States' => 13,
27             },
28             French => {
29             Canada => 26,
30             'United States' => 3,
31             },
32             },
33             Receiving => {
34             English => 56,
35             French => {
36             Canada => 12,
37             'United States' => 5,
38             },
39             },
40             };
41            
42            
43             tally( $data );
44            
45            
46             # $data now has the following value:
47             $data = {
48             Shipping => {
49             English => {
50             Canada => 8,
51             'United States' => 13,
52             tally => 21,
53             },
54             French => {
55             Canada => 26,
56             'United States' => 3,
57             tally => 29,
58             },
59             tally => {
60             Canada => 34,
61             'United States' => 16,
62             tally => 50,
63             },
64             },
65             Receiving => {
66             English => 56,
67             French => {
68             Canada => 12,
69             'United States' => 5,
70             tally => 17,
71             },
72             tally => 73,
73             },
74             tally => {
75             English => 77,
76             French => {
77             Canada => 38,
78             'United States' => 8,
79             tally => 46,
80             },
81             tally => 123,
82             },
83             };
84            
85            
86             =head1 DESCRIPTION
87              
88             =head2 tally ( @data )
89              
90             A method designed to calculate the tallies of hashes. It was originally
91             designed for reporting and statistical purposes.
92              
93             =cut
94              
95             sub tally {
96 77     77 1 127 my @data = grep { defined $_ } @_;
  111         198  
97 77         89 my @hashes = grep { ref $_ eq 'HASH' } @data;
  111         183  
98 77         85 my @scalars = grep { ref $_ eq '' } @data;
  111         184  
99            
100             # this will be the key within the given hashes where the sub-tallies will be stored
101             # TODO: make this value configurable
102             #
103 77         90 my $tally_field = 'tally';
104            
105             # in the case of scalars, we merely sum them together as would any numeric values
106 77 100       141 if (@scalars == @data) {
107 59         236 return List::Util::sum( @scalars );
108             }
109            
110             # we must be provided either hash references or scalars
111 18 50       37 unless (@hashes + @scalars == @data) {
112 0         0 die 'Data must be scalar or hash reference';
113             }
114            
115             # list all the unique keys found across all hash references
116 18         25 my @names = uniq( grep { $_ ne $tally_field } map { keys %$_ } @hashes );
  60         178  
  22         55  
117            
118             # compute the hash tallies
119 18         41 for my $hash (@hashes) {
120 22   50     27 $hash->{$tally_field} = tally( grep { defined $_ } map { $hash->{$_} } @names ) || 0;
121             }
122            
123             # compute the current tally using the previously calculated hash tallies
124 18         28 my %tally = ( $tally_field => tally( map { $_->{$tally_field} } @hashes ) );
  22         48  
125 18         27 for my $name (@names) {
126 36         41 $tally{$name} = tally( map { $_->{$name} } @hashes );
  44         95  
127             }
128            
129             # if we have scalars, we cannot return a hash because the data's
130             # granularity only goes as far as this iteration.
131             #
132 18 100       39 if (@scalars) {
133 5         6 my $tally = $tally{$tally_field};
134 5         12 $tally = $tally->{$tally_field} while ref $tally eq 'HASH';
135 5         31 return List::Util::sum( @scalars ) + $tally;
136             }
137             else {
138 13         53 return \%tally;
139             }
140             }
141              
142             =head1 AUTHOR
143            
144             Adam Paynter Eadapay@cpan.orgE
145            
146             =head1 COPYRIGHT AND LICENSE
147            
148             Copyright 2006 by Adam Paynter
149            
150             This library is free software; you can redistribute it and/or modify
151             it under the same terms as Perl itself.
152            
153             =cut
154              
155             1;