File Coverage

blib/lib/TableData/Object/aoaos.pm
Criterion Covered Total %
statement 173 173 100.0
branch 60 70 85.7
condition 5 6 83.3
subroutine 19 19 100.0
pod 14 15 93.3
total 271 283 95.7


line stmt bran cond sub pod time code
1             package TableData::Object::aoaos;
2              
3             our $DATE = '2020-05-29'; # DATE
4             our $VERSION = '0.112'; # VERSION
5              
6 4     4   97 use 5.010001;
  4         13  
7 4     4   19 use strict;
  4         6  
  4         86  
8 4     4   17 use warnings;
  4         7  
  4         143  
9              
10 4     4   364 use parent 'TableData::Object::Base';
  4         239  
  4         32  
11              
12             sub new {
13 27     27 1 4528 my ($class, $data, $spec) = @_;
14 27         76 my $self = bless {
15             data => $data,
16             spec => $spec,
17             }, $class;
18 27 100       56 if ($spec) {
19 17         32 $self->{cols_by_idx} = [];
20 17         27 my $ff = $spec->{fields};
21 17         47 for (keys %$ff) {
22 36         73 $self->{cols_by_idx}[ $ff->{$_}{pos} ] = $_;
23             }
24             $self->{cols_by_name} = {
25 17         39 map { $_ => $ff->{$_}{pos} }
  36         83  
26             keys %$ff
27             };
28             } else {
29 10 100       27 if (@$data) {
30 8         13 my $ncols = @{ $data->[0] };
  8         16  
31 8         24 $self->{cols_by_idx} = [ map {"column$_"} 0 .. $ncols-1 ];
  19         69  
32 8         22 $self->{cols_by_name} = { map {("column$_" => $_)} 0..$ncols-1 };
  19         58  
33             } else {
34 2         5 $self->{cols_by_idx} = [];
35 2         3 $self->{cols_by_name} = {};
36             }
37             }
38 27         194 $self;
39             }
40              
41             sub row_count {
42 7     7 1 13 my $self = shift;
43 7         7 scalar @{ $self->{data} };
  7         17  
44             }
45              
46             sub row {
47 4     4 1 1951 my ($self, $idx) = @_;
48 4         17 $self->{data}[$idx];
49             }
50              
51             sub row_as_aos {
52 22     22 1 1966 my ($self, $idx) = @_;
53 22         37 $self->{data}[$idx];
54             }
55              
56             sub row_as_hos {
57 4     4 1 1920 my ($self, $idx) = @_;
58 4         8 my $row_aos = $self->{data}[$idx];
59 4 100       10 return undef unless $row_aos;
60 3         6 my $cols = $self->{cols_by_idx};
61 3         4 my $row_hos = {};
62 3         5 for my $i (0..$#{$cols}) {
  3         8  
63 6         12 $row_hos->{$cols->[$i]} = $row_aos->[$i];
64             }
65 3         14 $row_hos;
66             }
67              
68             sub rows {
69 1     1 1 1898 my $self = shift;
70 1         6 $self->{data};
71             }
72              
73             sub rows_as_aoaos {
74 17     17 1 2530 my $self = shift;
75 17         151 $self->{data};
76             }
77              
78             sub rows_as_aohos {
79 7     7 1 2405 my $self = shift;
80 7         13 my $data = $self->{data};
81              
82 7         10 my $cols = $self->{cols_by_idx};
83 7         8 my $rows = [];
84 7         9 for my $aos (@{$self->{data}}) {
  7         14  
85 21         25 my $row = {};
86 21         26 for my $i (0..$#{$cols}) {
  21         32  
87 42         69 $row->{$cols->[$i]} = $aos->[$i];
88             }
89 21         28 push @$rows, $row;
90             }
91 7         42 $rows;
92             }
93              
94             sub uniq_col_names {
95 2     2 1 5 my ($self, $which) = @_;
96              
97 2         2 my @res;
98             COL:
99 2         3 for my $colname (sort keys %{$self->{cols_by_name}}) {
  2         9  
100 3         6 my $colidx = $self->{cols_by_name}{$colname};
101 3         4 my %mem;
102 3         4 for my $row (@{$self->{data}}) {
  3         6  
103 7 50       8 next COL unless $#{$row} >= $colidx;
  7         11  
104 7 100       15 next COL unless defined $row->[$colidx];
105 6 100       17 next COL if $mem{ $row->[$colidx] }++;
106             }
107 1         3 push @res, $colname;
108             }
109              
110 2         9 @res;
111             }
112              
113             sub const_col_names {
114 2     2 1 6 my ($self, $which) = @_;
115              
116 2         2 my @res;
117             COL:
118 2         4 for my $colname (sort keys %{$self->{cols_by_name}}) {
  2         9  
119 3         8 my $colidx = $self->{cols_by_name}{$colname};
120 3         4 my $i = -1;
121 3         4 my $val;
122             my $val_undef;
123 3         3 for my $row (@{$self->{data}}) {
  3         7  
124 8 50       9 next COL unless $#{$row} >= $colidx;
  8         18  
125 8         10 $i++;
126 8 100       11 if ($i == 0) {
127 3         5 $val = $row->[$colidx];
128 3 100       6 $val_undef = 1 unless defined $val;
129             } else {
130 5 100       7 if ($val_undef) {
131 2 50       5 next COL if defined;
132             } else {
133 3 50       5 next COL unless defined $row->[$colidx];
134 3 100       8 next COL unless $val eq $row->[$colidx];
135             }
136             }
137             }
138 2         14 push @res, $colname;
139             }
140              
141 2         11 @res;
142             }
143              
144             sub del_col {
145 4     4 1 12 my ($self, $name_or_idx) = @_;
146              
147 4         13 my $idx = $self->col_idx($name_or_idx);
148 4 100       13 return undef unless defined $idx;
149              
150 3         7 my $name = $self->{cols_by_idx}[$idx];
151              
152 3         3 for my $row (@{$self->{data}}) {
  3         7  
153 9         13 splice @$row, $idx, 1;
154             }
155              
156             # adjust cols_by_{name,idx}
157 3         5 for my $i (reverse 0..$#{$self->{cols_by_idx}}) {
  3         7  
158 6         10 my $name = $self->{cols_by_idx}[$i];
159 6 100       14 if ($i > $idx) {
    100          
160 2         4 $self->{cols_by_name}{$name}--;
161             } elsif ($i == $idx) {
162 3         4 splice @{ $self->{cols_by_idx} }, $i, 1;
  3         4  
163 3         7 delete $self->{cols_by_name}{$name};
164             }
165             }
166              
167             # adjust spec
168 3 50       7 if ($self->{spec}) {
169 3         5 my $ff = $self->{spec}{fields};
170 3         7 for my $name (keys %$ff) {
171 6 100       11 if (!exists $self->{cols_by_name}{$name}) {
172 3         8 delete $ff->{$name};
173             } else {
174 3         4 $ff->{$name}{pos} = $self->{cols_by_name}{$name};
175             }
176             }
177             }
178              
179 3         11 $name;
180             }
181              
182             sub rename_col {
183 4     4 1 978 my ($self, $old_name_or_idx, $new_name) = @_;
184              
185 4         11 my $idx = $self->col_idx($old_name_or_idx);
186 4 100       17 die "Unknown column '$old_name_or_idx'" unless defined($idx);
187 3         7 my $old_name = $self->{cols_by_idx}[$idx];
188 3 50       7 die "Please specify new column name" unless length($new_name);
189 3 100       9 return if $new_name eq $old_name;
190 2 100       15 die "New column name must not be a number" if $new_name =~ /\A\d+\z/;
191              
192 1         3 $self->{cols_by_idx}[$idx] = $new_name;
193 1         3 $self->{cols_by_name}{$new_name} = delete($self->{cols_by_name}{$old_name});
194 1 50       4 if ($self->{spec}) {
195 1         2 my $ff = $self->{spec}{fields};
196 1         3 $ff->{$new_name} = delete($ff->{$old_name});
197             }
198             }
199              
200             sub switch_cols {
201 4     4 1 962 my ($self, $name_or_idx1, $name_or_idx2) = @_;
202              
203 4         12 my $idx1 = $self->col_idx($name_or_idx1);
204 4 100       18 die "Unknown first column '$name_or_idx1'" unless defined($idx1);
205 3         8 my $idx2 = $self->col_idx($name_or_idx2);
206 3 100       16 die "Unknown second column '$name_or_idx2'" unless defined($idx2);
207 2 100       7 return if $idx1 == $idx2;
208              
209 1         5 my $name1 = $self->col_name($name_or_idx1);
210 1         3 my $name2 = $self->col_name($name_or_idx2);
211              
212             ($self->{cols_by_idx}[$idx1], $self->{cols_by_idx}[$idx2]) =
213 1         3 ($self->{cols_by_idx}[$idx2], $self->{cols_by_idx}[$idx1]);
214             ($self->{cols_by_name}{$name1}, $self->{cols_by_name}{$name2}) =
215 1         4 ($self->{cols_by_name}{$name2}, $self->{cols_by_name}{$name1});
216 1 50       4 if ($self->{spec}) {
217 1         2 my $ff = $self->{spec}{fields};
218 1         4 ($ff->{$name1}, $ff->{$name2}) = ($ff->{$name2}, $ff->{$name1});
219             }
220             }
221              
222             sub add_col {
223 5     5 1 3629 my ($self, $name, $idx, $spec) = @_;
224              
225 5 100       16 die "Column '$name' already exists" if defined $self->col_name($name);
226 4         10 my $col_count = $self->col_count;
227 4 100       9 if (defined $idx) {
228 3 100 100     27 die "Index must be between 0..$col_count"
229             unless $idx >= 0 && $idx <= $col_count;
230             } else {
231 1         2 $idx = $col_count;
232             }
233              
234 2         5 for (keys %{ $self->{cols_by_name} }) {
  2         7  
235 7 100       15 $self->{cols_by_name}{$_}++ if $self->{cols_by_name}{$_} >= $idx;
236             }
237 2         5 $self->{cols_by_name}{$name} = $idx;
238 2         3 splice @{ $self->{cols_by_idx} }, $idx, 0, $name;
  2         5  
239 2 50       6 if ($self->{spec}) {
240 2         3 my $ff = $self->{spec}{fields};
241 2         5 for my $f (values %$ff) {
242 7 100 66     22 $f->{pos}++ if defined($f->{pos}) && $f->{pos} >= $idx;
243             }
244 2 50       6 $ff->{$name} = defined($spec) ? {%$spec} : {};
245 2         5 $ff->{$name}{pos} = $idx;
246             }
247              
248 2         3 for my $row (@{ $self->{data} }) {
  2         4  
249 6         11 splice @$row, $idx, 0, undef;
250             }
251             }
252              
253             sub set_col_val {
254 2     2 0 369 my ($self, $name_or_idx, $value_sub) = @_;
255              
256 2         7 my $col_name = $self->col_name($name_or_idx);
257 2         5 my $col_idx = $self->col_idx($name_or_idx);
258              
259 2 100       14 die "Column '$name_or_idx' does not exist" unless defined $col_name;
260              
261 1         2 for my $i (0..$#{ $self->{data} }) {
  1         4  
262 2         15 my $row = $self->{data}[$i];
263 2         5 $row->[$col_idx] = $value_sub->(
264             table => $self,
265             row_idx => $i,
266             col_name => $col_name,
267             col_idx => $col_idx,
268             value => $row->[$col_idx],
269             );
270             }
271             }
272              
273             1;
274             # ABSTRACT: Manipulate array of arrays-of-scalars via table object
275              
276             __END__