File Coverage

blib/lib/Array/GroupBy.pm
Criterion Covered Total %
statement 51 51 100.0
branch 17 18 94.4
condition 9 10 90.0
subroutine 15 15 100.0
pod 3 3 100.0
total 95 97 97.9


line stmt bran cond sub pod time code
1             package Array::GroupBy;
2              
3 7     7   162713 use warnings;
  7         17  
  7         229  
4 7     7   38 use strict;
  7         27  
  7         398  
5              
6 7     7   189 use 5.008_008;
  7         27  
  7         302  
7              
8 7     7   36 use List::Util qw(max);
  7         12  
  7         744  
9              
10 7     7   6158 use version; our $VERSION = qv('0.0.4');
  7         18721  
  7         41  
11              
12 7     7   680 use base qw( Exporter );
  7         16  
  7         1126  
13             our @EXPORT = qw( igroup_by );
14             our @EXPORT_OK = qw( igroup_by str_row_equal num_row_equal);
15              
16             # Copyright Stanford University. March 8th, 2012
17             # All rights reserved.
18             # Author: Sam Brain
19              
20 7     7   42 use Carp;
  7         14  
  7         707  
21 7     7   8365 use Params::Validate qw(:all);
  7         91272  
  7         5190  
22              
23             ########################################
24             sub igroup_by {
25 6     6 1 464 my %opts = validate(@_, {data => { type => ARRAYREF },
26             compare => { type => CODEREF },
27             args => { type => ARRAYREF, optional => 1 },
28             }
29             );
30              
31 6         54 my ($data, $compare, $args) = @opts{qw(data compare args)};
32              
33 6 50       22 croak "The array passed to igroup_by( data => ... ) is empty, called"
34             unless @$data;
35              
36 6         14 my $previous_line = $data->[0];
37              
38 6         10 my $i = 1; # index into @$data
39              
40             return sub {
41 29     29   18303 my @result;
42             my $line;
43              
44 29 100       81 return unless $previous_line;
45              
46 23         48 push @result, $previous_line;
47              
48 23         2729 while ($line = $data->[$i++]) {
49             last
50 40 100       96 unless $compare->($previous_line, $line, $args);
51              
52 23         196 push @result, $line;
53             }
54             # line was different from previous, or end-of-data
55 23         91 $previous_line = $line;
56              
57 23         61 return \@result;
58             }
59 6         48 }
60              
61             ########################################
62 38     38 1 167 sub str_row_equal { return _row_equal( sub { $_[0] eq $_[1] }, @_ ) }
  21     21   149  
63 14     14 1 59 sub num_row_equal { return _row_equal( sub { $_[0] == $_[1] }, @_ ) }
  9     9   46  
64              
65             ########################################
66             sub _row_equal {
67 30     30   54 my ($is_equal, $first, $second, $slice) = @_;
68              
69 30 100       98 return 0 if @$first != @$second;
70              
71 28 100 100     143 $slice = [ 0 .. $#$first] unless $slice and @$slice;
72              
73             #print "_row_equal(): slice @$slice, ", max(@$slice), ", ", $#$first, " \n";
74              
75             # slice out of bounds
76 28 100       134 return 0 if max(@$slice) > $#$first;
77              
78             #print "_row_equal(): slice @$slice\n";
79              
80 27         60 foreach (@$slice) {
81             # both undef
82 61 100 100     269 next unless defined $first->[$_] or defined $second->[$_];
83              
84             # one defined, one not
85 58 100 75     303 return 0 if defined $first->[$_] xor defined $second->[$_];
86              
87             #print "first ", $first->[$_], " second ", $second->[$_], "\n";
88              
89 52 100       122 return 0 unless $is_equal->($first->[$_], $second->[$_]);
90             }
91              
92 12         61 return 1;
93             }
94              
95             1;
96              
97             __END__