File Coverage

blib/lib/TableData/Object/aohos.pm
Criterion Covered Total %
statement 175 175 100.0
branch 58 66 87.8
condition 5 6 83.3
subroutine 19 19 100.0
pod 14 15 93.3
total 271 281 96.4


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