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