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