File Coverage

blib/lib/TableData/Object/aoaos.pm
Criterion Covered Total %
statement 158 160 98.7
branch 58 68 85.2
condition 5 6 83.3
subroutine 15 16 93.7
pod 11 12 91.6
total 247 262 94.2


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