File Coverage

blib/lib/Text/NumericList.pm
Criterion Covered Total %
statement 100 113 88.5
branch 25 34 73.5
condition n/a
subroutine 14 16 87.5
pod 10 10 100.0
total 149 173 86.1


line stmt bran cond sub pod time code
1             package Text::NumericList;
2              
3 1     1   12916 use 5.006;
  1         5  
  1         49  
4 1     1   10 use strict;
  1         2  
  1         40  
5 1     1   6 use warnings;
  1         8  
  1         156  
6              
7             our $VERSION = '0.01';
8              
9             # Object creator
10             sub new{
11 2     2 1 100 my $self = shift;
12 2         13 my $pkg = {
13             array => [],
14             string => '',
15             mask => undef,
16             output_separator => ',',
17             input_separator => ',',
18             range_regexp => '\-',
19             range_symbol => '-'
20             };
21 2         5 bless $pkg, $self;
22 2         6 return $pkg;
23             }
24              
25             # Convert array to the string. Set array and string attributes
26             sub set_array{
27 1     1   5 no warnings;
  1         2  
  1         1600  
28 15     15 1 36 my ($self, @array) = @_;
29              
30             # In each array element, remove all non-digit characters.
31 15         123 foreach (@array){ $_ =~ s/\D//g; }
  75         129  
32              
33             # Remove non-unique elements.
34 15         21 my %seen;
35 15 100       21 @array = grep {!$seen{$_}++ and $_ ne ''} @array;
  75         466  
36              
37             # Sort the array.
38 15         40 @array = sort {$a <=> $b} @array;
  110         118  
39              
40             # Set 'array' attribute.
41 15         42 $self->{'array'} = [@array];
42              
43             # Apply the mask to the array.
44 15         50 $self->_mask;
45 15         32 @array = $self->get_array;
46              
47             # Replace 3 and more sequential elements with a range string (e.g.
48             # '1-5') using range_symbol attribute.
49 15         27 my @string;
50             my @tmp;
51 15         21 push @tmp, shift @array;
52 15         31 while (@array){
53 58         90 push @tmp, shift @array;
54 58 100       165 if ($tmp[$#tmp] - $tmp[0] > $#tmp){
55 19         31 unshift @array, pop @tmp;
56 19         39 push @string, $self->_range(@tmp);
57 19         28 @tmp = ();
58 19         50 push @tmp, shift @array;
59             }
60             }
61 15         30 push @string, $self->_range(@tmp);
62 15         91 $self->{'string'} = join($self->{'output_separator'}, @string);
63             }
64              
65             # If the array has more than 3 elements, join them with range symbol.
66             # Otherwise, return the array.
67             sub _range {
68 34     34   68 my ($self, @array) = @_;
69 34 100       65 if (@array < 3){return @array}
  21         50  
70 13         50 return join($self->{'range_symbol'}, $array[0], $array[$#array]);
71             }
72              
73             # Return the array attribute
74             sub get_array{
75 52     52 1 62 my $self = shift;
76 52         55 return @{$self->{'array'}};
  52         240  
77             }
78              
79             # Convert the string into an array. Set string and array attributes.
80             sub set_string{
81 8     8 1 585 my ($self, $string) = @_;
82              
83 8         14 my @array;
84              
85             # Convert the string to lower case:
86 8         12 $string = lc($string);
87              
88             # If the range is set and the string matches 'all', 'odd', or 'even',
89             # set the 'all', 'odd', or 'even' flag. Remove 'all', 'odd', or
90             # 'even' from the string.
91 8         22 my $all = ($string =~ s/all//gi);
92 8         64 my $odd = ($string =~ s/odd//gi);
93 8         16 my $even = ($string =~ s/even//gi);
94              
95             # If 'all' flag is set, the set_array($self->{'mask'}->get_array)
96             # method and return.
97 8 100       30 if ( ref $self->{'mask'} eq ref $self){
98 7         17 my @mask_array = $self->{'mask'}->get_array;
99 7 50       18 if ($all){
100             # Add the whole mask array
101 0         0 push @array, @mask_array;
102             }
103 7 100       16 if ($odd){
104             # Add odd numbers from the mask array
105 1         3 push @array, grep {$_%2} @mask_array;
  10         16  
106             }
107 7 100       22 if ($even){
108             # Add even numbers from the mask array
109 1         3 push @array, grep {!($_%2)} @mask_array;
  10         22  
110             }
111             }
112              
113             # Unless range regular expression is '..',
114             # replace all instances of '..' with a space
115 8 100       23 unless ($self->{'range_regexp'} eq '\.\.'){
116 7         12 $string =~ s/\.\./ /g;
117             }
118              
119             # Replace all instances of string with '_'
120 8         66 $string =~ s/$self->{'range_regexp'}+/_/g;
121              
122             # $string =~ s/[^\d$is]*_[^\d$is]*/_/g;
123 8         17 my $is = $self->{'input_separator'};
124 8         121 $string =~ s/[^\d$is]*_[^\d$is]*/_/g;
125              
126             # Replace all instances of one or more non-digit and non-'..'
127             # with a comma
128 8         35 $string =~ s/[^\d_]+/,/g;
129            
130             # Remove '_' not surrounded with digits.
131 8         14 $string =~ s/,_/,/g;
132 8         11 $string =~ s/_,/,/g;
133              
134             # Replace '_\d+_' with a single '_'
135 8         12 $string =~ s/_\d+_/_/g;
136              
137             # Replace all '_' with a '..'
138 8         14 $string =~ s/_/../g;
139              
140             # Split the string using /,/:
141 8         23 push @array, map {eval "($_)"} split(/,/, $string);
  17         1116  
142              
143 8         28 $self->set_array(@array);
144 8         30 $self->{'n'} = 0;
145             }
146              
147             # Return the string attribute
148             sub get_string{
149 8     8 1 28 my $self = shift;
150 8         27 return $self->{'string'};
151             }
152              
153             # Set output separator attribute
154             sub set_output_separator{
155 1     1 1 81 my ($self, $separator) = @_;
156 1 50       3 if ($separator){
157 1         59 $self->{'string'} =~ s/$self->{'output_separator'}/$separator/g;
158 1         4 $self->{'output_separator'} = $separator;
159             } else {
160 0         0 warn "Output separator is empty. Output separator is unchanged.";
161             }
162             }
163              
164             # Set input separator attribute
165             sub set_input_separator{
166 0     0 1 0 my ($self, $separator) = @_;
167 0 0       0 if ($separator){
168 0         0 $self->{'input_separator'} = $separator;
169             } else {
170 0         0 warn "Input separator is empty. Input separator is unchanged.";
171             }
172             }
173              
174             # Set range symbol attribute
175             sub set_range_symbol{
176 0     0 1 0 my ($self, $symbol) = @_;
177 0 0       0 if ($symbol){
178 0         0 $self->{'string'} =~ s/$self->{'range_symbol'}/$symbol/g;
179 0         0 $self->{'range_symbol'} = $symbol;
180             }
181             else {
182 0         0 warn "Range symbol is empty. Range symbol is left unchanged."
183             }
184             }
185              
186             # Set range regular expression
187             sub set_range_regexp{
188 1     1 1 225 my ($self, $regexp) = @_;
189 1 50       3 if ($regexp){
190 1         4 $self->{'range_regexp'} = $regexp;
191             }
192             else {
193 0         0 warn 'Range regular expression is empty. Range regular expression is left unchanged.';
194             }
195             }
196              
197             # Set object mask
198             sub set_mask{
199 1     1 1 6 my ($self, $mask) = @_;
200 1 50       5 if (ref $mask eq ref $self){
201 1         3 $self->{'mask'} = $mask;
202 1         22 $self->_mask;
203             } else {
204 0         0 warn "The mask is not a ".ref $self." object. The mask is left unchanged.";
205             }
206             }
207              
208             # Apply the mask to the array
209             sub _mask{
210 16     16   24 my $self = shift;
211 16 100       46 unless (ref $self->{'mask'} eq ref $self){return}
  1         3  
212 15         29 my @array = $self->get_array;
213 15         35 my @mask_array = $self->{'mask'}->get_array;
214 15 50       49 if (@mask_array){
215 15         16 my (@union, %union, @isect, %isect);
216 15         23 @union = @isect = ();
217 15         21 %union = %isect = ();
218              
219 15 100       29 foreach my $e (@array, @mask_array){ $union{$e}++ && $isect{$e}++ }
  214         467  
220              
221 15 100       93 $self->set_array(sort {$a <=> $b} keys %isect) unless $self->{'n'}++;
  42         60  
222             }
223             }
224              
225              
226             1;
227             __END__