File Coverage

blib/lib/TableData/Object/aohos.pm
Criterion Covered Total %
statement 160 162 98.7
branch 56 64 87.5
condition 5 6 83.3
subroutine 15 16 93.7
pod 11 12 91.6
total 247 260 95.0


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