File Coverage

blib/lib/Array/2D.pm
Criterion Covered Total %
statement 492 717 68.6
branch 131 206 63.5
condition 72 103 69.9
subroutine 74 92 80.4
pod 70 70 100.0
total 839 1188 70.6


line stmt bran cond sub pod time code
1             ## no critic (ProhibitExcessMainComplexity)
2             # it thinks all the code in $x = sub {...} is in the main module
3             package Array::2D;
4 22     22   420626 use 5.008001;
  22         95  
5 22     22   125 use strict;
  22         51  
  22         478  
6 22     22   131 use warnings;
  22         50  
  22         1424  
7              
8             our $VERSION = '0.001_005';
9             $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval)
10              
11             ## no critic (RequirePodAtEnd)
12              
13             =encoding utf8
14              
15             =head1 NAME
16              
17             Array::2D - Methods for simple array-of-arrays data structures
18              
19             =head1 VERSION
20              
21             This documentation refers to version 0.001_005
22              
23             =head2 NOTICE
24              
25             This is alpha software. Method names and behaviors are subject to change.
26             The test suite has significant omissions.
27              
28             =head1 SYNOPSIS
29              
30             use Array::2D;
31             my $array2d = Array::2D->new( [ qw/a b c/ ] , [ qw/w x y/ ] );
32              
33             # $array2d contains
34              
35             # a b c
36             # w x y
37              
38             $array2d->push_col (qw/d z/);
39              
40             # a b c d
41             # w x y z
42              
43             say $array2d->[0][1];
44             # prints "b"
45              
46             =head1 DESCRIPTION
47              
48             Array::2D is a module that adds useful methods to Perl's
49             standard array of arrays ("AoA") data structure, as described in
50             L and
51             L. That is, an array that
52             contains other arrays:
53              
54             [
55             [ 1, 2, 3 ] ,
56             [ 4, 5, 6 ] ,
57             ]
58              
59             This module provides methods for using that standard construction.
60              
61             Most of the time, it's good practice to avoid having programs that
62             use a module know about the internal construction of an object.
63             However, this module is not like that. It assumes that the data
64             structure I accessible outside the module's code, and may be
65             altered by other code. The module will never change the data
66             structure to include anything else. Therefore, it is perfectly
67             reasonable to use the normal reference syntax to access items inside
68             the array. A construction like C<< $array2d->[0][1] >> for accessing
69             a single element, or C<< @{$array2d} >> to get the list of rows,
70             is perfectly appropriate. This module exists because the reference-based
71             implementation of multidimensional arrays in Perl makes it difficult
72             to access, for example, a single column, or a two-dimensional slice,
73             without writing lots of extra code.
74              
75             Array::2D uses "row" for the first dimension, and "column" or
76             "col" for the second dimension. This does mean that the order
77             of (row, column) is the opposite of the usual (x,y) algebraic order.
78              
79             Because this object is just an array of arrays, most of the methods
80             referring to rows are here mainly for completeness, and aren't
81             much more useful than the native Perl construction (e.g., C<<
82             $array2d->last_row() >> is just a slower way of doing C<< $#{$array2d}
83             >>.) They will also typically be much slower.
84              
85             On the other hand, most of the methods referring to columns are useful,
86             since there's no simple way of fetching a column or columns in Perl.
87              
88             =head2 PADDING
89              
90             Because it is intended that the structure can be altered by standard
91             Perl constructions, there is no guarantee that the object is either
92             completely padded out so that every value within the structure's
93             height and width has a value (undefined or not), alternatively
94             completely pruned so that there are as few undefined values as
95             possible. The only padding that must exist is padding to ensure that
96             the row and column indexes are correct for all defined values.
97              
98             Other Perl code could change the padding state at any time, or leave
99             it in an intermediate state (where some padding exists, but the
100             padding is not complete).
101              
102             For example, the following would be valid:
103              
104             $array2d = [
105             [ undef, 1, 2 ],
106             3 ],
107             [ 4, 6, ],
108             ];
109              
110             The columns would be returned as (undef, 3, 4), (1, undef, 6), and (2).
111              
112             There are methods to set padding -- the C method
113             will eliminate padding, and the C method will pad out
114             the array to the highest row and column with a defined value.
115              
116             Methods that retrieve data will prune the data before returning it.
117              
118             Methods that delete rows or columns (del_*, shift_*, pop_*, and in void
119             context, slice) will prune not only the returned data but also the
120             array itself.
121              
122             =cut
123              
124             # core modules
125 22     22   131 use Carp;
  22         62  
  22         1528  
126 22     22   132 use List::Util(qw/max min/);
  22         96  
  22         2118  
127 22     22   10089 use POSIX (qw/floor ceil/);
  22         132196  
  22         133  
128 22     22   32065 use Scalar::Util(qw/blessed reftype/);
  22         56  
  22         1834  
129              
130             # non-core modules
131 22     22   10946 use List::MoreUtils 0.28 (qw/natatime any all none/);
  22         174339  
  22         300  
132 22     22   32511 use Params::Validate(qw/validate ARRAYREF HASHREF/);
  22         176515  
  22         3692  
133              
134             ### Test for Ref::Util and if present, use it
135             BEGIN {
136             my $impl = $ENV{PERL_ARRAY_2D_NO_REF_UTIL}
137 22   66 22   399 || our $NO_REF_UTIL;
138              
139 22 100 66     102 if ( !$impl && eval { require Ref::Util; 1 } ) {
  20         8764  
  20         28385  
140 20         134022 Ref::Util->import(qw/is_arrayref is_plain_arrayref/);
141             # There is a possibility that Ref::Util will change the meaning
142             # of is_arrayref to "is_plain_arrayref" and create a new
143             # is_any_arrayref that means what is_arrayref means now.
144             # Changes will have to be made in that event.
145             }
146             else {
147 2     54   8 *is_plain_arrayref = sub { ref( $_[0] ) eq 'ARRAY' };
  54         146  
148 2     25   11691 *is_arrayref = sub { reftype( $_[0] ) eq 'ARRAY' };
  25         126  
149             }
150             }
151              
152             ### Test for Unicode::GCString and if present, use it
153              
154             ### First, the variable $text_columns_cr is declared.
155             ### Then, it is set to a reference to code that
156             ### a) determines what the future text_columns code should be,
157             ### b) sets the variable $text_column_cr to point to that new code, and
158             ### c) then jumps to that new code.
159              
160             ### Thus the first time it's run, it basically redefines itself
161             ### to be the proper routine (either one with or without Unicode::GCString).
162              
163             my $text_columns_cr;
164             $text_columns_cr = sub {
165              
166             my $impl = $ENV{PERL_ARRAY_2D_NO_GCSTRING}
167             || our $NO_GCSTRING;
168              
169             if ( !$impl && eval { require Unicode::GCString; 1 } ) {
170             $text_columns_cr = sub {
171              
172             return 0 unless defined $_[0];
173             my $cols = Unicode::GCString->new("$_[0]")->columns;
174             return $cols;
175              
176             # explicit stringification is necessary
177             # since Unicode::GCString doesn't automatically
178             # stringify numbers
179             };
180             }
181             else {
182             $text_columns_cr = sub {
183             return 0 unless defined $_[0];
184             return length( $_[0] );
185             };
186             }
187             goto $text_columns_cr;
188              
189             };
190              
191             =head1 METHODS
192              
193             Some general notes:
194              
195             =over
196              
197             =item *
198              
199             Except for constructor methods, all methods can be called as an object
200             method on a blessed Array::2D object:
201              
202             $array_obj->clone();
203              
204             Or as a class method, if one supplies the array of arrays as the first
205             argument:
206              
207             Array::2D->clone($array);
208              
209             In the latter case, the array of arrays need not be blessed (and will not
210             be blessed by Array::2D).
211              
212             =item *
213              
214             In all cases where an array of arrays is specified as an argument
215             (I), this can be either an Array::2D object or a regular
216             array of arrays data structure that is not an object.
217              
218             =item *
219              
220             Where rows are columns are removed from the array (as with any of the
221             C, C, C methods), time-consuming assemblage of
222             return values is ommitted in void context.
223              
224             =item *
225              
226             Some care is taken to ensure that rows are not autovivified. Normally, if the
227             highest row in an arrayref-of-arrayrefs is 2, and a program
228             attempts to read the value of $aoa->[3]->[$anything], Perl will create
229             an empty third row. This module avoids autovification from just reading data.
230             This is the only advantage of methods like C, C, etc. compared
231             to regular Perl constructions.
232              
233             =item *
234              
235             It is assumed that row and column indexes passed to the methods are integers.
236             If they are negative, they will count from the end instead of
237             the beginning, as in regular Perl array subscripts. Specifying a negative
238             index that is off the beginning of the array (e.g., specifying column -6
239             on an array whose width is 5) will cause an exception to be thrown.
240             This is different than specifying an index is off the end of the array --
241             reading column #5 of a three-column array will return an empty column,
242             and trying to write to tha column will pad out the intervening columns
243             with undefined values.
244              
245             The behavior of the module when anything other than an integer is
246             passed in (strings, undef, floats, NaN, objects, etc.) is unspecified.
247             Don't do that.
248              
249             =back
250              
251             =head2 BASIC CONSTRUCTOR METHODS
252              
253             =over
254              
255             =item B)>
256              
257             =item B )>
258              
259             Returns a new Array::2D object. It accepts a list of array
260             references as arguments, which become the rows of the object.
261              
262             If it receives only one argument, and that argument is an array of
263             arrays -- that is, a reference to an unblessed array, and in turn
264             that array only contains references to unblessed arrays -- then the
265             arrayrefs contained in that structure are made into the rows of a new
266             Array::2D object.
267              
268             If you want it to bless an existing arrayref-of-arrayrefs, use
269             C. If you don't want to reuse the existing arrayrefs as
270             the rows inside the object, use C.
271              
272             If you think it's possible that the detect-an-AoA-structure could
273             give a false positive (you want a new object that might have only one row,
274             where each entry in that row is an reference to an unblessed array),
275             use C<< Array::2D->bless ( [ @your_rows ] ) >>.
276              
277             =cut
278              
279             sub new {
280              
281 126 100 100 126 1 106666 if ( 2 == @_
      100        
282             and is_plain_arrayref( $_[1] )
283 30     30   221 and all { is_plain_arrayref($_) } @{ $_[1] } )
  22         102  
284             {
285 4         29 my $class = shift;
286 4         9 my $aoa = shift;
287              
288 4         9 my $self = [ @{$aoa} ];
  4         13  
289 4         11 CORE::bless $self, $class;
290 4         14 return $self;
291             }
292              
293 122         488 goto &bless;
294              
295             }
296              
297             =item B)>
298              
299             =item B)>
300              
301             Just like new(), except that if passed a single arrayref which contains
302             only other arrayrefs, it will bless the outer arrayref and return it.
303             This saves the time and memory needed to copy the rows.
304              
305             Note that this blesses the original array, so any other references to
306             this data structure will become a reference to the object, too.
307              
308             =cut
309              
310             ## no critic (RequireTrailingCommaAtNewline)
311             # eliminates a PPI false positive -- it thinks bless { ... } is a hashref
312              
313             sub bless { ## no critic (Subroutines::ProhibitBuiltInHomonyms)
314              
315 136     136 1 19292 my $class = shift;
316              
317 136         289 my @rows = @_;
318              
319 136 100       340 if ( 0 == @rows ) { # if no arguments, new anonymous AoA
320 4         12 return $class->empty;
321             }
322              
323 132 100       317 if ( 1 == @rows ) {
324 28         111 my $blessing = blessed( $rows[0] );
325 28 100 66     108 if ( defined($blessing) and $blessing eq $class ) {
326             # already an object
327 4         14 return $rows[0];
328             }
329              
330 24 100 66     116 if ( is_plain_arrayref( $rows[0] )
331 28     28   165 and all { is_plain_arrayref($_) } @{ $rows[0] } )
  24         75  
332             {
333 4         36 return CORE::bless $rows[0], $class;
334             }
335             }
336              
337 124 100   379   704 if ( any { not is_plain_arrayref($_) } @rows ) {
  379         1887  
338 4         465 croak "Arguments to $class->new or $class->blessed "
339             . 'must be unblessed arrayrefs (rows)';
340             }
341              
342 120         909 return CORE::bless [@rows], $class;
343              
344             } ## tidy end: sub bless
345              
346             ## use critic
347              
348             =item B
349              
350             Returns a new, empty Array::2D object.
351              
352             =cut
353              
354             sub empty {
355 22     22 1 2312 my $class = shift;
356 22         118 return CORE::bless [], $class;
357             }
358              
359             =item B)>
360              
361             Takes a flat list and returns it as an Array::2D object,
362             where each row has the number of elements specified. So, for example,
363              
364             Array::2D->new_across (3, qw/a b c d e f g h i j/)
365              
366             returns
367              
368             [
369             [ a, b, c] ,
370             [ d, e, f] ,
371             [ g, h, i] ,
372             [ j ],
373             ]
374              
375             =cut
376              
377             sub new_across {
378 2     2 1 3434 my $class = shift;
379              
380 2         4 my $quantity = shift;
381 2         7 my @values = @_;
382              
383 2         4 my $self;
384 2         12 my $it = natatime( $quantity, @values );
385 2         23 while ( my @vals = $it->() ) {
386 6         30 push @{$self}, [@vals];
  6         27  
387             }
388              
389 2         12 CORE::bless $self, $class;
390 2         9 return $self;
391              
392             }
393              
394             =item B)>
395              
396             Takes a flat list and returns it as an Array::2D object,
397             where each column has the number of elements specified. So, for
398             example,
399              
400             Array::2D->new_down (3, qw/a b c d e f g h i j/)
401              
402             returns
403              
404             [
405             [ a, d, g, j ] ,
406             [ b, e, h ] ,
407             [ c, f, i ] ,
408             ]
409              
410             =cut
411              
412             sub new_down {
413 8     8 1 4903 my $class = shift;
414              
415 8         16 my $quantity = shift;
416 8         37 my @values = @_;
417              
418 8         16 my $self;
419 8         47 my $it = natatime( $quantity, @values );
420              
421 8         111 while ( my @vals = $it->() ) {
422 37         196 for my $i ( 0 .. $#vals ) {
423 170         218 push @{ $self->[$i] }, $vals[$i];
  170         386  
424             }
425             }
426              
427 8         40 CORE::bless $self, $class;
428 8         34 return $self;
429              
430             }
431              
432             =item B
433              
434             A combination of C and C. Takes three named
435             arguments:
436              
437             =over
438              
439             =item array => I
440              
441             A one-dimensional list of scalars.
442              
443             =item separator => I
444              
445             A scalar to be passed to ->tabulate_equal_width(). The default is
446             a single space.
447              
448             =item width => I
449              
450             The width of the terminal. If not specified, defaults to 80.
451              
452             =back
453              
454             The method determines the number of text columns required, creates an
455             Array::2D object of that number of text columns using new_down, and then
456             returns first the object and then the results of ->tabulate_equal_width()
457             on that object.
458              
459             See L
460             below for information on how the widths of text in text columns
461             are determined.
462              
463             =cut
464              
465             sub new_to_term_width {
466              
467 6     6 1 19749 my $class = shift;
468 6         223 my %params = validate(
469             @_,
470             { array => { type => ARRAYREF },
471             width => { default => 80 },
472             separator => { default => q[ ] },
473             },
474             );
475              
476 6         35 my $array = $params{array};
477              
478 6         17 my $separator = $params{separator};
479 6         20 my $sepwidth = $text_columns_cr->($separator);
480 6         16 my $colwidth = $sepwidth + max( map { $text_columns_cr->($_) } @$array );
  146         225  
481 6   50     65 my $cols = floor( ( $params{width} + $sepwidth ) / ($colwidth) ) || 1;
482              
483             # add sepwidth there to compensate for the fact that we don't actually
484             # print the separator at the end of the line
485              
486 6         26 my $rows = ceil( @$array / $cols );
487              
488 6         28 my $array2d = $class->new_down( $rows, @$array );
489              
490 6         23 my $tabulated = $array2d->tabulate_equal_width($separator);
491              
492 6         35 return $array2d, $tabulated;
493              
494             } ## tidy end: sub new_to_term_width
495              
496             =item B<<< new_from_tsv(I) >>>
497              
498             Returns a new object from a string containing tab-separated values.
499             The string is first split into lines and then split into values by tabs.
500              
501              
502             Lines can be separated by by carriage returns, line feeds, a CR/LF pair, or
503             other characters matching Perl's \R (see L).
504              
505             If multiple strings are provided, they will be considered additional lines. So,
506             if one has already read a TSV file, one can pass the entire contents, the
507             series of lines in the TSV file, or a combination of two.
508              
509             Note that this is not a routine that reads TSV I, just TSV
510             I, which may or may not have been read from a file. See
511             C()> for a method that reads TSV
512             files (and other kinds).
513              
514             =cut
515              
516             sub new_from_tsv {
517 0     0 1 0 my $class = shift;
518 0         0 my @lines = map { split(/\R/) } @_;
  0         0  
519              
520 0         0 my $self = [ map { [ split(/\t/) ] } @lines ];
  0         0  
521              
522 0         0 CORE::bless $self, $class;
523 0         0 return $self;
524             }
525              
526             =back
527              
528             =head2 CONSTRUCTOR METHODS THAT READ FILES
529              
530             =over
531              
532             =item B<<< new_from_xlsx(I) >>>
533              
534             This method requires that L
535             be installed on the local system.
536              
537             Returns a new object from a worksheet in an Excel XLSX file, consisting
538             of the rows and columns of that sheet. The I parameter
539             is passed directly to the C<< ->worksheet >> method of
540             C, which accepts a name or an index. If nothing
541             is passed, it requests sheet 0 (the first sheet).
542              
543             =cut
544              
545             sub new_from_xlsx {
546 0     0 1 0 my $class = shift;
547 0         0 my $xlsx_filespec = shift;
548 0   0     0 my $sheet_requested = shift || 0;
549              
550             # || handles empty strings
551              
552 0 0       0 croak 'No file specified in ' . __PACKAGE__ . '->new_from_xlsx'
553             unless $xlsx_filespec;
554              
555 0         0 require Spreadsheet::ParseXLSX; ### DEP ###
556              
557 0         0 my $parser = Spreadsheet::ParseXLSX->new;
558 0         0 my $workbook = $parser->parse($xlsx_filespec);
559              
560 0 0       0 if ( !defined $workbook ) {
561 0         0 croak $parser->error();
562             }
563              
564 0         0 my $sheet = $workbook->worksheet($sheet_requested);
565              
566 0 0       0 if ( !defined $sheet ) {
567 0         0 croak "Sheet $sheet_requested not found in $xlsx_filespec in "
568             . __PACKAGE__
569             . '->new_from_xlsx';
570             }
571              
572 0         0 my ( $minrow, $maxrow ) = $sheet->row_range();
573 0         0 my ( $mincol, $maxcol ) = $sheet->col_range();
574              
575 0         0 my @rows;
576              
577 0         0 foreach my $row ( $minrow .. $maxrow ) {
578              
579 0         0 my @cells = map { $sheet->get_cell( $row, $_ ) } ( $mincol .. $maxcol );
  0         0  
580              
581 0         0 foreach (@cells) {
582 0 0       0 if ( defined $_ ) {
583 0         0 $_ = $_->value;
584             }
585             else {
586 0         0 $_ = q[];
587             }
588             }
589              
590 0         0 push @rows, \@cells;
591              
592             }
593              
594 0         0 return $class->bless( \@rows );
595              
596             } ## tidy end: sub new_from_xlsx
597              
598             =item B<<< new_from_file(I, I) >>>
599              
600             Returns a new object from a file on disk, specified as I.
601              
602             If I is present, then it must be either 'xlsx' or 'tsv', and it
603             will read the file assuming it is of that type.
604              
605             If no I is present, it will attempt to use the file's
606             extension to determine the proper filetype. Any file whose extension is
607             '.xlsx' will be treated as type 'xlsx', and any file whose extension is
608             either '.tab' or '.tsv' will be treated as type 'tsv'.
609              
610             For the moment, it will also assume that a file whose extension is '.txt'
611             is of type 'tsv'. It should be assumed that future versions
612             may attempt to determine whether the file is more likely to be a comma-separated
613             values file instead. To ensure that the file will be treated as tab-separated,
614             pass in a filetype explicitly.
615              
616             If the file type is 'xlsx', this method
617             passes that file on to C and requests the first worksheet.
618              
619             If the file type is 'tsv',
620             it slurps the file in memory and passes the result to C.
621             This uses L, which mus be installed on the system.
622              
623             =cut
624              
625             my $filetype_from_ext_r = sub {
626             my $filespec = shift;
627             return unless $filespec;
628              
629             my ($ext) = $filespec =~ m[
630             [.] # a dot
631             ([^.]+) # one or more non-dot characters
632             \z # end of the string
633             ]x;
634              
635             my $fext = fc($ext);
636              
637             if ( $fext eq fc('xlsx') ) {
638             return 'xlsx';
639             }
640              
641             if ( any { $fext eq fc($_) } qw/tsv tab txt/ ) {
642             return 'tsv';
643             }
644              
645             return;
646              
647             };
648              
649             sub new_from_file {
650 0     0 1 0 my $class = shift;
651 0         0 my $filespec = shift;
652 0   0     0 my $filetype = shift || $filetype_from_ext_r->($filespec);
653              
654 0 0       0 croak "Cannot determine type of $filespec in "
655             . __PACKAGE__
656             . '->new_from_file'
657             unless $filetype;
658              
659 0 0       0 if ( $filetype eq 'xlsx' ) {
660 0         0 return $class->new_from_xlsx($filespec);
661             }
662              
663 0 0       0 if ( $filetype eq 'tsv' ) {
664 0         0 require File::Slurper; ### DEP ###
665 0         0 my $tsv = File::Slurper::read_text($filespec);
666 0         0 return $class->new_from_tsv($tsv);
667             }
668              
669 0         0 croak "File type $filetype unrecognized in "
670             . __PACKAGE__
671             . '->new_from_file';
672              
673             } ## tidy end: sub new_from_file
674              
675             ################################################################
676             ### shim allowing being called as either class or object method
677              
678             my $invocant_cr = sub {
679             my $invocant = shift;
680             my $blessing = blessed $invocant;
681              
682             return ( $blessing, $invocant ) if defined $blessing;
683             # invocant is an object blessed into the $blessing class
684              
685             my $array2d = shift;
686             return ( $invocant, $array2d ) if is_arrayref($array2d);
687             # invocant is a class
688              
689             ## no critic (ProhibitMagicNumbers)
690             croak 'No array passed to ' . ( caller(1) )[3];
691              
692             };
693              
694             =back
695              
696             =head2 COPYING AND REARRANGING ARRAYS
697              
698             =over
699              
700             =item B
701              
702             Returns new object which has copies of the data in the 2D array object.
703             The 2D array will be different, but if any of the elements of the 2D
704             array are themselves references, they will refer to the same things as
705             in the original 2D array.
706              
707             =cut
708              
709             sub clone {
710 464     464 1 1043522 my ( $class, $self ) = &$invocant_cr;
711 464         902 my $new = [ map { [ @{$_} ] } @{$self} ];
  2037         2639  
  2037         4948  
  464         1028  
712 464         1057 CORE::bless $new, $class;
713 464         1644 return $new;
714             }
715              
716             =item B
717              
718             Returns an unblessed array containing the same rows as the 2D
719             array object. If called as a class method and given an argument that is
720             already unblessed, will return the argument. Otherwise will create
721             a new, unblessed array.
722              
723             This is usually pointless, as Perl lets you ignore the object-ness of
724             any object and access the data inside, but sometimes certain modules
725             don't like to break object encapsulation, and this will allow getting
726             around that.
727              
728             Note that while modifying the elements inside the rows will modify the
729             original 2D array, modifying the outer arrayref will not (unless
730             that arrayref was not blessed in the first place). So:
731              
732             my $unblessed = $array2d->unblessed;
733              
734             $unblessed->[0][0] = 'Up in the corner';
735             # modifies original object
736              
737             $unblessed->[0] = [ 'Up in the corner ' , 'Yup'];
738             # does not modify original object
739              
740             This can be confusing, so it's best to avoid modifying the result of
741             C. Use C instead.
742              
743             =cut
744              
745             sub unblessed {
746 2     2 1 8050 my ( $class, $self ) = &$invocant_cr;
747 2 100       8 return $self if not blessed $self;
748 1         3 my $new = [ @{$self} ];
  1         2  
749 1         3 return $new;
750             }
751              
752             =item B
753              
754             Returns a new, unblessed, array of arrays containing copies of the data
755             in the 2D array object.
756              
757             The array of arrays will be different, but if any of the elements of
758             the 2D array are themselves references, they will refer to the same
759             things as in the original 2D array.
760              
761             =cut
762              
763             sub clone_unblessed {
764 358     358 1 8684 my ( $class, $self ) = &$invocant_cr;
765 358         654 my $new = [ map { [ @{$_} ] } @{$self} ];
  1691         2171  
  1691         3459  
  358         679  
766 358         1846 return $new;
767             }
768              
769             =item B
770              
771             Transposes the array: the elements that used to be
772             in rows are now in columns, and vice versa.
773              
774             In void context, alters the original. Otherwise, creates a new
775             Array::2D object and returns that.
776              
777             The result of transpose() is pruned.
778              
779             =cut
780              
781             sub transpose {
782 24     24 1 43025 my ( $class, $self ) = &$invocant_cr;
783              
784 24 100       39 unless ( @{$self} ) {
  24         67  
785 4 100       20 return $class->empty if defined wantarray;
786 2         5 return $self;
787             }
788              
789 20         37 my $new = [];
790              
791 20         61 foreach my $col ( 0 .. $class->last_col($self) ) {
792 88         107 push @{$new}, [ map { $_->[$col] } @{$self} ];
  88         112  
  464         700  
  88         124  
793             }
794              
795 20         70 $class->prune($new);
796              
797             # non-void context: return new object
798 20 100       46 if ( defined wantarray ) {
799 10         14 CORE::bless $new, $class;
800 10         25 return $new;
801             }
802              
803             # void context: alter existing array
804 10         15 @{$self} = @{$new};
  10         33  
  10         15  
805 10         27 return;
806              
807             } ## tidy end: sub transpose
808              
809             =item B
810              
811             Returns the array as a single, one-dimensional flat list of all the defined
812             values. Note that it does not flatten any arrayrefs that are deep inside
813             the 2D structure -- just the rows and columns of the structure itself.
814              
815             =cut
816              
817             sub flattened {
818 10     10 1 14668 my ( $class, $self ) = &$invocant_cr;
819 10         22 my @flattened = map { @{$_} } @$self;
  36         40  
  36         66  
820 10         25 return grep { defined $_ } @flattened;
  70         126  
821             }
822              
823             =back
824              
825             =head2 DIMENSIONS OF THE ARRAY
826              
827             =over
828              
829             =item B
830              
831             Returns a true value if the array is empty, false otherwise.
832              
833             =cut
834              
835             sub is_empty {
836 20     20 1 5638 my ( $class, $self ) = &$invocant_cr;
837 20         102 return not( scalar @$self );
838             }
839              
840             =item B
841              
842             Returns the number of rows in the array. The same as C.
843              
844             =cut
845              
846             sub height {
847 20     20 1 5778 my ( $class, $self ) = &$invocant_cr;
848 20         96 return scalar @$self;
849             }
850              
851             =item B
852              
853             Returns the number of columns in the array. (The number of elements in
854             the longest row.)
855              
856             =cut
857              
858             sub width {
859 334     334 1 6454 my ( $class, $self ) = &$invocant_cr;
860 334 100       561 return 0 unless @{$self};
  334         863  
861 314         521 return max( map { scalar @{$_} } @{$self} );
  1292         1578  
  1292         2745  
  314         589  
862             }
863              
864             =item B
865              
866             Returns the index of the last row of the array. If the array is
867             empty, returns -1. The same as C<$#{$array}>.
868              
869             =cut
870              
871             sub last_row {
872 186     186 1 6150 my ( $class, $self ) = &$invocant_cr;
873 186         332 return $#{$self};
  186         746  
874             }
875              
876             =item B
877              
878             Returns the index of the last column of the array. (The index of the
879             last element in the longest row.) If the array is
880             empty, returns -1.
881              
882             =cut
883              
884             sub last_col {
885 202     202 1 6444 my ( $class, $self ) = &$invocant_cr;
886 202 100       291 return -1 unless @{$self};
  202         483  
887 198         290 return max( map { $#{$_} } @{$self} );
  1242         1404  
  1242         2225  
  198         371  
888             }
889              
890             =back
891              
892             =head2 READING ELEMENTS, ROWS, COLUMNS, SLICES
893              
894             =over
895              
896             =item B)>
897              
898             Returns the element in the given row and column. A slower way of
899             saying C<< $array2d->[I][I] >>, except that it avoids
900             autovivification. Like that construct, it will return undef if the element
901             does not already exist.
902              
903             =cut
904              
905             sub element {
906             ## no critic (ProhibitExplicitReturnUndef)
907 32     32 1 51155 my ( $class, $self ) = &$invocant_cr;
908              
909 32         49 my $row_idx = shift;
910             return undef
911 32 100 100     87 unless -@$self <= $row_idx and $row_idx <= $#{$self};
  30         105  
912 26         37 my $col_idx = shift;
913             return undef
914 26         70 unless -@{ $self->[$row_idx] } <= $col_idx
915 26 100 100     29 and $col_idx <= $#{ $self->[$row_idx] };
  24         81  
916 20         51 return $self->[$row_idx][$col_idx];
917             }
918              
919             =item B)>
920              
921             Returns the elements in the given row. A slower way of saying C<<
922             @{$array2d->[I]} >>, except that it avoids autovivification.
923              
924             =cut
925              
926             sub row {
927 36     36 1 31113 my ( $class, $self ) = &$invocant_cr;
928 36         59 my $row_idx = shift;
929             return ()
930             unless -@$self <= $row_idx
931 36 100 100     94 and $row_idx <= $#{$self};
  34         124  
932             # if empty, will test (0 <= $col_idx and $col_idx <= -1) which is always false
933 30         41 my @row = @{ $self->[$row_idx] };
  30         66  
934 30   33     130 pop @row while @row and not defined $row[-1]; # prune
935 30         87 return @row;
936             }
937              
938             =item B)>
939              
940             Returns the elements in the given column.
941              
942             =cut
943              
944             sub col {
945 104     104 1 36209 my ( $class, $self ) = &$invocant_cr;
946              
947 104         188 my $col_idx = shift;
948 104         242 my $width = $class->width($self);
949             return ()
950 104 100 100     528 unless -$width <= $col_idx
951             and $col_idx < $width;
952             # if empty, will test (0 <= $col_idx and $col_idx < 0) which is always false
953              
954 84 100       183 $col_idx += $width if $col_idx < 0;
955             # make into offset from beginning, not the end
956             # Must do this because otherwise, counts from end of *this row*, not end of
957             # whole array
958              
959             my @col
960 406 100 66     816 = map { ( 0 <= $col_idx && $col_idx <= $#{$_} ) ? $_->[$col_idx] : undef }
961 84         107 @{$self};
  84         179  
962             # the element if it's valid in that row, otherwise undef
963 84   66     550 pop @col while @col and not defined $col[-1]; # prune
964 84         304 return @col;
965             } ## tidy end: sub col
966              
967             =item B<< rows(I) >>
968              
969             Returns a new Array::2D object with all the columns of the
970             specified rows.
971              
972             Note that duplicates are not de-duplicated, so the result of
973             $obj->rows(1,1,1) will be three copies of the same row.
974              
975             =cut
976              
977             sub rows {
978 90     90 1 65725 my ( $class, $self ) = &$invocant_cr;
979 90         213 my @row_indices = @_;
980              
981             my $rows
982             = $class->new(
983 90 100 100     160 map { ( -@$self <= $_ && $_ <= $#{$self} ) ? $self->[$_] : [] }
  210         502  
984             @row_indices );
985             # the row if it's a valid row idx, othewise an empty ref
986 90         272 $rows->prune();
987 90         208 return $rows;
988             }
989              
990             =item B, ...)>
991              
992             Returns a new Array::2D object with the specified columns. This is transposed
993             from the original array's order, so each column requested will be in its own
994             row.
995              
996             $array = [
997             [ qw/ a b c d / ],
998             [ qw/ j k l m / ],
999             [ qw/ w x y z / ],
1000             ];
1001             my $cols = Array::2D->cols($array, 1, 2);
1002             # $cols = bless [ [ qw/ b k x / ] , [ qw/ c l y / ] ], 'Array::2D';
1003              
1004             Note that duplicates are not de-duplicated, so the result of
1005             $obj->cols(1,1,1) will retrieve three copies of the same column.
1006              
1007             =cut
1008              
1009             sub cols {
1010 30     30 1 54498 my ( $class, $self ) = &$invocant_cr;
1011 30         73 my @col_indices = @_;
1012              
1013 30         68 my $cols = [ map { [ $class->col( $self, $_ ) ] } @col_indices ];
  58         150  
1014              
1015 30         69 CORE::bless $cols, $class;
1016 30         101 $cols->prune;
1017 30         104 return $cols;
1018             }
1019              
1020             =item B, ...)>
1021              
1022             Returns a new Array::2D object with the specified columns of each row.
1023             Unlike C, the result of this method is not transposed.
1024              
1025             $array = [
1026             [ qw/ a b c d / ],
1027             [ qw/ j k l m / ],
1028             [ qw/ w x y z / ],
1029             ];
1030             my $sliced_cols = Array::2D->slice_cols($array, 1, 2);
1031             # $sliced_cols = bless [
1032             # [ qw/ b c / ] ,
1033             # [ qw/ k l / ] ,
1034             # [ qw/ x y / ] ,
1035             # ], 'Array::2D';
1036              
1037             Note that duplicates are not de-duplicated, so the result of
1038             $obj->slice_cols(1,1,1) will retrieve three copies of the same column.
1039              
1040             =cut
1041              
1042             sub slice_cols {
1043 84     84 1 65420 my ( $class, $self ) = &$invocant_cr;
1044 84         192 my @col_indices = @_;
1045 84         212 my $width = $class->width($self);
1046 84         190 for my $col_idx (@col_indices) {
1047 182 100       405 $col_idx += $width if $col_idx < 0;
1048             }
1049             # must adjust this to whole array width, not just row width
1050              
1051 84         170 my $return = [];
1052              
1053 84         163 foreach my $row_r (@$self) {
1054 354         417 my @new_row;
1055 354         451 foreach my $col_idx (@col_indices) {
1056 806 100 100     2461 if ( -$width <= $col_idx and $col_idx < $width ) {
1057 706         1215 push @new_row, $row_r->[$col_idx];
1058             }
1059             else {
1060 100         136 push @new_row, undef;
1061             }
1062             }
1063 354         653 push @$return, \@new_row;
1064             }
1065              
1066 84         153 CORE::bless $return, $class;
1067 84         221 $return->prune;
1068 84         188 return $return;
1069             } ## tidy end: sub slice_cols
1070              
1071             =item B)>
1072              
1073             Takes a two-dimensional slice of the array; like cutting a rectangle
1074             out of the array.
1075              
1076             In void context, alters the original array, which then will contain
1077             only the area specified; otherwise, creates a new Array::2D
1078             object and returns the object.
1079              
1080             Negative indicies are treated as though they mean that many from the end:
1081             the last item is -1, the second-to-last is -2, and so on.
1082              
1083             Slices are always returned in the order of the original array, so
1084             $obj->slice(0,1,0,1) is the same as $obj->slice(1,0,1,0).
1085              
1086             =cut
1087              
1088             sub slice {
1089 84     84 1 153642 my ( $class, $self ) = &$invocant_cr;
1090              
1091 84         188 my ( $firstrow, $lastrow, $firstcol, $lastcol, ) = @_;
1092              
1093             ### adjust row indices
1094              
1095 84         212 my $self_lastrow = $class->last_row($self);
1096              
1097 84         167 foreach my $row_idx ( $firstrow, $lastrow ) {
1098 168 100       377 next unless $row_idx < 0;
1099 24         45 $row_idx += $self_lastrow + 1;
1100             }
1101              
1102             ### adjust col indices
1103              
1104 84         200 my $self_lastcol = $class->last_col($self);
1105              
1106 84         160 foreach my $col ( $firstcol, $lastcol ) {
1107 168 100       358 next unless $col < 0;
1108 24         52 $col += $self_lastcol + 1;
1109             }
1110              
1111             ### sort indices
1112              
1113 84 100       181 ( $firstrow, $lastrow ) = ( $lastrow, $firstrow )
1114             if $lastrow < $firstrow;
1115              
1116 84 100       177 ( $firstcol, $lastcol ) = ( $lastcol, $firstcol )
1117             if $lastcol < $firstcol;
1118              
1119             # if it's specifying an area entirely off the beginning or end
1120             # of the array, return empty
1121 84 100 100     634 if ( $lastrow < 0
      100        
      100        
1122             or $self_lastrow < $firstrow
1123             or $lastcol < 0
1124             or $self_lastcol < $firstcol )
1125             {
1126 24 100       98 return $class->empty() if defined wantarray;
1127 12         34 @{$self} = ();
  12         51  
1128 12         37 return;
1129             }
1130              
1131             # otherwise, since it's at least partially in the array, set the rows
1132             # to be within the array.
1133 60 100       138 $lastrow = $self_lastrow if $self_lastrow < $lastrow;
1134 60 50       122 $firstrow = 0 if $firstrow < 0;
1135              
1136 60         187 my $rows = $class->rows( $self, $firstrow .. $lastrow );
1137              
1138             # set the bounds to be within the column of these rows
1139 60 50       135 $firstcol = 0 if $firstcol < 0;
1140 60         141 my $rows_lastcol = $class->last_col($rows);
1141 60 100       145 $lastcol = $rows_lastcol if $rows_lastcol < $lastcol;
1142              
1143 60         162 my $new = $class->slice_cols( $rows, $firstcol .. $lastcol );
1144 60 100       226 return $new if defined wantarray;
1145 30         43 @{$self} = @{$new};
  30         113  
  30         46  
1146 30         122 return;
1147             } ## tidy end: sub slice
1148              
1149             =back
1150              
1151             =head2 SETTING ELEMENTS, ROWS, COLUMNS, SLICES
1152              
1153             None of these methods return anything. At some point it might
1154             be worthwhile to have them return the old values of whatever they changed
1155             (when not called in void context), but they don't do that yet.
1156              
1157             =over
1158              
1159             =item B)>
1160              
1161             Sets the element in the given row and column to the given value.
1162             Just a slower way of saying
1163             C<< $array2d->[I][I] = I >>.
1164              
1165             =cut
1166              
1167             sub set_element {
1168 18     18 1 18394 my ( $class, $self ) = &$invocant_cr;
1169 18         33 my $row_idx = shift;
1170 18         27 my $col_idx = shift;
1171 18         82 $self->[$row_idx][$col_idx] = shift;
1172 12         31 return;
1173             }
1174              
1175             =item B)>
1176              
1177             Sets the given row to the given set of values.
1178             A slower way of saying C<< {$array2d->[I] = [ @values ] >>.
1179              
1180             =cut
1181              
1182             sub set_row {
1183 20     20 1 19260 my ( $class, $self ) = &$invocant_cr;
1184 20   100     69 my $row_idx = shift || 0;
1185 20         56 my @elements = @_;
1186 20 50       54 return $#{$self} unless @elements;
  0         0  
1187 20         75 $self->[$row_idx] = \@elements;
1188 18         55 return;
1189             }
1190              
1191             =item B)>
1192              
1193             Sets the given column to the given set of values. If more values are given than
1194             there are rows, will add rows; if fewer values than there are rows, will set the
1195             entries in the remaining rows to C.
1196              
1197             =cut
1198              
1199             sub set_col {
1200 26     26 1 32002 my ( $class, $self ) = &$invocant_cr;
1201 26         62 my $col_idx = shift;
1202 26         78 my @elements = @_;
1203              
1204             # handle negative col_idx
1205              
1206 26         91 my $width = $class->width($self);
1207 26 50       81 return $width unless @elements;
1208              
1209 26 100       78 if ( $col_idx < -$width ) {
1210 2         206 croak("$class->set_col: negative index off the beginning of the array");
1211             }
1212 24 100       66 $col_idx += $width if $col_idx < 0;
1213              
1214 24         81 for my $row_idx ( 0 .. max( $class->last_row($self), $#elements ) ) {
1215 76         230 $self->[$row_idx][$col_idx] = $elements[$row_idx];
1216             }
1217 24         122 return;
1218              
1219             } ## tidy end: sub set_col
1220              
1221             =item B<< set_rows(I) >>
1222              
1223             =item B<< set_rows(I) >>
1224              
1225             Sets the rows starting at the given start row index to the rows given.
1226             So, for example, $obj->set_rows(1, $row_ref_a, $row_ref_b) will set
1227             row 1 of the object to be the elements of $row_ref_a and row 2 to be the
1228             elements of $row_ref_b.
1229              
1230             The arguments after I are passed to C, so it accepts
1231             any of the arguments that C accepts.
1232              
1233             Returns the height of the array.
1234              
1235             =cut
1236              
1237             sub set_rows {
1238 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1239 0         0 my $self_start_row_idx = shift;
1240 0         0 my $given = $class->new(@_);
1241 0         0 my @given_rows = @{$given};
  0         0  
1242 0         0 for my $given_row_idx ( 0 .. $#given_rows ) {
1243 0         0 my @elements = @{ $given_rows[$given_row_idx] };
  0         0  
1244 0         0 $self->[ $self_start_row_idx + $given_row_idx ] = \@elements;
1245             }
1246 0         0 return;
1247             }
1248              
1249             =item B...)>
1250              
1251             Sets the columns starting at the given start column index to the columns given.
1252             So, for example, $obj->set_cols(1, $col_ref_a, $col_ref_b) will set
1253             column 1 of the object to be the elemnents of $col_ref_a and column 2 to be the
1254             elements of $col_ref_b.
1255              
1256             =cut
1257              
1258             sub set_cols {
1259 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1260 0         0 my $self_start_col_idx = shift;
1261 0         0 my @given_cols = @_;
1262 0         0 my $width;
1263              
1264 0         0 foreach my $given_col_idx ( 0 .. $#given_cols ) {
1265 0         0 my @given_elements = @{ $given_cols[$given_col_idx] };
  0         0  
1266 0         0 $width = $class->set_col( $self, $self_start_col_idx + $given_col_idx,
1267             @given_elements );
1268             }
1269 0         0 return;
1270             }
1271              
1272             =item B)>
1273              
1274             =item B)>
1275              
1276             Sets a rectangular segment of the object to have the values of the supplied
1277             rows or array of arrays, beginning at the supplied first row and first column.
1278             The arguments after the row and columns are passed to C, so it accepts
1279             any of the arguments that C accepts.
1280              
1281             =cut
1282              
1283             sub set_slice {
1284 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1285              
1286 0         0 my $class_firstrow = shift;
1287 0         0 my $class_firstcol = shift;
1288              
1289 0         0 my $slice = $class->new(@_);
1290 0         0 my $slice_last_row = $slice->last_row;
1291 0         0 my $slice_last_col = $slice->last_col;
1292              
1293 0         0 for my $row_idx ( 0 .. $slice_last_row ) {
1294 0         0 for my $col_idx ( 0 .. $slice_last_col ) {
1295 0         0 $self->[ $class_firstrow + $row_idx ][ $class_firstcol + $col_idx ]
1296             = $slice->[$row_idx][$col_idx];
1297             }
1298             }
1299              
1300 0         0 return;
1301              
1302             } ## tidy end: sub set_slice
1303              
1304             =back
1305              
1306             =head2 INSERTING ROWS AND COLUMNS
1307              
1308             All these methods return the new number of either rows or columns.
1309              
1310             =over
1311              
1312             =item B)>
1313              
1314             Adds the specified elements as a new row at the given index.
1315              
1316             =cut
1317              
1318             sub ins_row {
1319 20     20 1 40639 my ( $class, $self ) = &$invocant_cr;
1320 20         55 my $row_idx = shift;
1321 20         78 my @row = @_;
1322              
1323 20 100       44 if ( $#{$self} < $row_idx ) {
  20         74  
1324 6         28 $self->[$row_idx] = [@row];
1325             }
1326             else {
1327 14         33 splice( @{$self}, $row_idx, 0, [@row] );
  14         96  
1328             }
1329              
1330 18         42 return scalar @{$self};
  18         79  
1331             }
1332              
1333             =item B)>
1334              
1335             Adds the specified elements as a new column at the given index.
1336              
1337             =cut
1338              
1339             sub ins_col {
1340 30     30 1 35025 my ( $class, $self ) = &$invocant_cr;
1341 30         74 my $col_idx = shift;
1342 30         96 my @col = @_;
1343              
1344             # handle negative col_idx
1345 30         106 my $width = $class->width($self);
1346 30 50       93 return $width unless @col;
1347              
1348 30 100       104 if ( $col_idx < -$width ) {
1349 2         274 croak("$class->ins_col: negative index off the beginning of the array");
1350             }
1351 28 100       80 $col_idx += $width if $col_idx < 0;
1352              
1353 28         92 my $last_row = max( $class->last_row($self), $#col );
1354             # if this is below the array, extend the array so it is longer
1355 28         63 $#{$self} = $last_row;
  28         91  
1356              
1357 28         108 for my $row_idx ( 0 .. $last_row ) {
1358             # if this is off to the right of this row,
1359 84 100       173 if ( $#{ $self->[$row_idx] } < $col_idx ) {
  84         263  
1360             # just set the element
1361 24         85 $self->[$row_idx][$col_idx] = $col[$row_idx];
1362             }
1363             else {
1364             # otherwise, insert it in using splice
1365 60         115 splice( @{ $self->[$row_idx] }, $col_idx, 0, $col[$row_idx] );
  60         255  
1366             }
1367             }
1368              
1369 28 50       132 return $class->width($self) if defined wantarray;
1370 0         0 return;
1371             } ## tidy end: sub ins_col
1372              
1373             =item B)>
1374              
1375             Takes the specified array of arrays and inserts them as new rows at the
1376             given index.
1377              
1378             The arguments after the row index are passed to C, so it accepts
1379             any of the arguments that C accepts.
1380              
1381             =cut
1382              
1383             sub ins_rows {
1384 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1385 0         0 my $row_idx = shift;
1386 0         0 my $given = $class->new(@_);
1387              
1388 0         0 splice( @{$self}, $row_idx, 0, @$given );
  0         0  
1389 0         0 return scalar @{$self};
  0         0  
1390             }
1391              
1392             =item B)>
1393              
1394             Takes the specified array of arrays and inserts them as new columns at
1395             the given index.
1396              
1397             =cut
1398              
1399             sub ins_cols {
1400 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1401 0         0 my $col_idx = shift;
1402 0         0 my @cols = @_;
1403              
1404 0         0 my $last_row = max( $class->last_row($self), map { $#{$_} } @cols );
  0         0  
  0         0  
1405              
1406 0         0 for my $row_idx ( 0 .. $last_row ) {
1407 0         0 for my $col (@cols) {
1408 0         0 splice( @{ $self->[$row_idx] }, $col_idx, 0, $col->[$row_idx] );
  0         0  
1409             }
1410             }
1411 0 0       0 return $class->width($self) if defined wantarray;
1412 0         0 return;
1413             }
1414              
1415             =item B)>
1416              
1417             Adds the specified elements as the new first row.
1418              
1419             =cut
1420              
1421             sub unshift_row {
1422 10     10 1 13936 my ( $class, $self ) = &$invocant_cr;
1423 10         33 my @col_values = @_;
1424 10         17 return unshift @{$self}, \@col_values;
  10         45  
1425             }
1426              
1427             =item B)>
1428              
1429             Adds the specified elements as the new first column.
1430              
1431             =cut
1432              
1433             sub unshift_col {
1434 10     10 1 16229 my ( $class, $self ) = &$invocant_cr;
1435 10         32 my @col_values = @_;
1436 10         42 return $class->ins_col( $self, 0, @col_values );
1437             }
1438              
1439             =item B)>
1440              
1441             =item B)>
1442              
1443             Takes the specified array of arrays and adds them as new rows before
1444             the beginning of the existing rows. Returns the new number of rows.
1445              
1446             The arguments are passed to C, so it accepts
1447             any of the arguments that C accepts.
1448              
1449             =cut
1450              
1451             sub unshift_rows {
1452 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1453 0         0 my $given = $class->new(@_);
1454 0         0 return unshift @{$self}, @$given;
  0         0  
1455             }
1456              
1457             =item B)>
1458              
1459             Takes the specified array of arrays and adds them as new columns,
1460             before the beginning of the existing columns. Returns the new number of
1461             columns.
1462              
1463             =cut
1464              
1465             sub unshift_cols {
1466 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1467 0         0 my @cols = @_;
1468 0         0 return $class->ins_cols( $self, 0, @cols );
1469             }
1470              
1471             =item B)>
1472              
1473             Adds the specified elements as the new final row. Returns the new
1474             number of rows.
1475              
1476             =cut
1477              
1478             sub push_row {
1479 10     10 1 18938 my ( $class, $self ) = &$invocant_cr;
1480 10         45 my @col_values = @_;
1481 10         24 return push @{$self}, \@col_values;
  10         59  
1482             }
1483              
1484             =item B)>
1485              
1486             Adds the specified elements as the new final column. Returns the new
1487             number of columns.
1488              
1489             =cut
1490              
1491             sub push_col {
1492 10     10 1 20664 my ( $class, $self ) = &$invocant_cr;
1493 10         41 my @col = @_;
1494 10         45 my $width = $class->width($self);
1495 10 50       40 return $width unless @col;
1496              
1497 10         44 for my $row_idx ( 0 .. max( $class->last_row($self), $#col ) ) {
1498 30         115 $self->[$row_idx][$width] = $col[$row_idx];
1499             }
1500              
1501 10         60 return $width + 1; # new width
1502             }
1503              
1504             =item B)>
1505              
1506             =item B)>
1507              
1508             Takes the specified array of arrays and adds them as new rows after the
1509             end of the existing rows. Returns the new number of rows.
1510              
1511             The arguments are passed to C, so it accepts
1512             any of the arguments that C accepts.
1513              
1514             =cut
1515              
1516             sub push_rows {
1517 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1518 0         0 my $rows = $class->new(@_);
1519 0         0 return push @{$self}, @$rows;
  0         0  
1520             }
1521              
1522             =item B)>
1523              
1524             Takes the specified array of arrays and adds them as new columns, after
1525             the end of the existing columns. Returns the new number of columns.
1526              
1527             =cut
1528              
1529             sub push_cols {
1530 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1531 0         0 my @cols = @_;
1532 0         0 my $col_idx = $class->last_col($self);
1533              
1534 0 0       0 if ( -1 == $col_idx ) {
1535 0         0 @{$self} = map { [ @{$_} ] } @{$self};
  0         0  
  0         0  
  0         0  
  0         0  
1536 0 0       0 return $class->width($self) if defined wantarray;
1537 0         0 return;
1538             }
1539              
1540 0         0 my $last_row = max( $class->last_row($self), $#cols );
1541 0         0 my $last_col = $class->last_col($self);
1542              
1543 0         0 for my $row_index ( 0 .. $last_row ) {
1544 0         0 my $row_r = $self->[$row_index];
1545 0 0       0 if ( not defined $row_r ) {
1546 0         0 $row_r = $self->[$row_index] = [];
1547             }
1548 0         0 $#{$row_r} = $last_col; # pad out
  0         0  
1549 0         0 push @{$row_r}, @{ $cols[$row_index] };
  0         0  
  0         0  
1550             }
1551              
1552 0 0       0 return $class->width($self) if defined wantarray;
1553 0         0 return;
1554              
1555             } ## tidy end: sub push_cols
1556              
1557             =back
1558              
1559             =head2 RETRIEVING AND DELETING ROWS AND COLUMNS
1560              
1561             =over
1562              
1563             =item B)>
1564              
1565             Removes the row of the object specified by the index and returns a list
1566             of the elements of that row.
1567              
1568             =cut
1569              
1570             sub del_row {
1571 22     22 1 24083 my ( $class, $self ) = &$invocant_cr;
1572 22         36 my $row_idx = shift;
1573              
1574 22 100       33 return () unless @{$self};
  22         56  
1575 20 100       54 return () if $class->last_row($self) < $row_idx;
1576              
1577 18 100       42 if ( defined wantarray ) {
1578 16         37 my @deleted = $class->row( $self, $row_idx );
1579 16         25 splice( @{$self}, $row_idx, 1 );
  16         31  
1580 16         51 $class->prune($self);
1581 16   33     67 pop @deleted while @deleted and not defined $deleted[-1]; # prune
1582 16         56 return @deleted;
1583             }
1584              
1585 2         4 splice( @{$self}, $row_idx, 1 );
  2         16  
1586 0         0 $class->prune($self);
1587 0         0 return;
1588             }
1589              
1590             =item B)>
1591              
1592             Removes the column of the object specified by the index and returns a
1593             list of the elements of that column.
1594              
1595             =cut
1596              
1597             sub del_col {
1598 32     32 1 21557 my ( $class, $self ) = &$invocant_cr;
1599 32         59 my $col_idx = shift;
1600              
1601             # handle negative col_idx
1602 32         76 my $width = $class->width($self);
1603 32 100       78 return () if $width <= $col_idx;
1604              
1605 28 100       68 if ( $col_idx < -$width ) {
1606 2         200 croak("$class->del_col: negative index off the beginning of the array");
1607             }
1608 26 100       52 $col_idx += $width if $col_idx < 0;
1609              
1610 26         37 my @deleted;
1611 26 50       54 if ( defined wantarray ) {
1612 26         65 @deleted = $class->col( $self, $col_idx );
1613 26   33     103 pop @deleted while @deleted and not defined $deleted[-1]; # prune
1614             }
1615              
1616 26         39 foreach my $row ( @{$self} ) {
  26         50  
1617 78         103 splice( @{$row}, $col_idx, 1 );
  78         127  
1618             }
1619 26         70 $class->prune($self);
1620              
1621 26 50       116 return @deleted if defined wantarray;
1622 0         0 return;
1623             } ## tidy end: sub del_col
1624              
1625             =item B, I...)>
1626              
1627             Removes the rows of the object specified by the indices. Returns an
1628             Array::2D object of those rows.
1629              
1630             =cut
1631              
1632             sub del_rows {
1633 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1634 0         0 my @row_idxs = @_;
1635              
1636 0 0       0 unless (@$self) {
1637 0 0       0 return $class->empty if defined wantarray;
1638 0         0 return;
1639             }
1640              
1641 0         0 my $deleted;
1642 0 0       0 if ( defined wantarray ) {
1643 0         0 $deleted = $class->rows( $self, @row_idxs );
1644             }
1645              
1646 0         0 foreach my $row_idx (@row_idxs) {
1647 0         0 splice( @{$self}, $row_idx, 1 );
  0         0  
1648             }
1649              
1650 0         0 $class->prune($self);
1651 0 0       0 return $deleted if defined wantarray;
1652 0         0 return;
1653             } ## tidy end: sub del_rows
1654              
1655             =item B, I...)>
1656              
1657             Removes the columns of the object specified by the indices. Returns an
1658             Array::2D object of those columns.
1659              
1660             =cut
1661              
1662             sub del_cols {
1663 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
1664 0         0 my @col_idxs = @_;
1665              
1666 0 0       0 unless (@$self) {
1667 0 0       0 return $class->empty if defined wantarray;
1668 0         0 return;
1669             }
1670              
1671 0         0 my $deleted;
1672 0 0       0 if ( defined wantarray ) {
1673 0         0 $deleted = $class->cols( $self, @col_idxs );
1674             }
1675              
1676 0         0 foreach my $col_idx ( reverse sort @_ ) {
1677 0         0 $self->del_col($col_idx);
1678             }
1679              
1680 0         0 $class->prune($self);
1681 0 0       0 return $deleted if defined wantarray;
1682 0         0 return;
1683             } ## tidy end: sub del_cols
1684              
1685             =item B
1686              
1687             Removes the first row of the object and returns a list of the elements
1688             of that row.
1689              
1690             =cut
1691              
1692             sub shift_row {
1693 12     12 1 11802 my ( $class, $self ) = &$invocant_cr;
1694 12 100       21 return () unless @{$self};
  12         32  
1695 10         17 my @row = @{ shift @{$self} };
  10         12  
  10         26  
1696 10   33     52 pop @row while @row and not defined $row[-1];
1697 10         30 $class->prune($self);
1698 10         32 return @row;
1699             }
1700              
1701             =item B
1702              
1703             Removes the first column of the object and returns a list of the
1704             elements of that column.
1705              
1706             =cut
1707              
1708             sub shift_col {
1709 12     12 1 11798 my ( $class, $self ) = &$invocant_cr;
1710 12         18 my @col = map { shift @{$_} } @{$self};
  30         36  
  30         60  
  12         25  
1711 12   100     61 pop @col while @col and not defined $col[-1]; # prune
1712 12         32 $class->prune($self);
1713 12         40 return @col;
1714             }
1715              
1716             =item B
1717              
1718             Removes the last row of the object and returns a list of the elements
1719             of that row.
1720              
1721             =cut
1722              
1723             sub pop_row {
1724 12     12 1 11862 my ( $class, $self ) = &$invocant_cr;
1725 12 100       23 return () unless @{$self};
  12         32  
1726 10         12 my @row = @{ pop @{$self} };
  10         17  
  10         22  
1727 10   33     54 pop @row while @row and not defined $row[-1]; # prune
1728 10         30 $class->prune($self);
1729 10         34 return @row;
1730             }
1731              
1732             =item B
1733              
1734             Removes the last column of the object and returns a list of the
1735             elements of that column.
1736              
1737             =cut
1738              
1739             sub pop_col {
1740 12     12 1 12040 my ( $class, $self ) = &$invocant_cr;
1741 12 100       21 return () unless @{$self};
  12         32  
1742 10         27 my $last_col = $class->last_col($self);
1743 10 50       24 return () if -1 == $last_col;
1744 10         26 $class->prune($self);
1745 10         25 return $class->del_col( $self, $last_col );
1746             }
1747              
1748             =back
1749              
1750             =head2 ADDING OR REMOVING PADDING
1751              
1752             Padding, here, means empty values beyond
1753             the last defined value of each column or row. What counts as "empty"
1754             depends on the method being used.
1755              
1756             =over
1757              
1758             =item B
1759              
1760             Occasionally an array of arrays can end up with final rows or columns
1761             that are entirely undefined. For example:
1762              
1763             my $obj = Array::2D->new ( [ qw/a b c/] , [ qw/f g h/ ]);
1764             $obj->[0][4] = 'e';
1765             $obj->[3][0] = 'k';
1766              
1767             # a b c undef e
1768             # f g h
1769             # (empty)
1770             # k
1771              
1772             $obj->pop_row();
1773             $obj->pop_col();
1774              
1775             # a b c undef
1776             # f g h
1777             # (empty)
1778              
1779             That would yield an object with four columns, but in which the last
1780             column and last row (each with index 3) consists of only undefined
1781             values.
1782              
1783             The C method eliminates these entirely undefined or empty
1784             columns and rows at the end of the object.
1785              
1786             In void context, alters the original object. Otherwise, creates a new
1787             Array::2D object and returns the object.
1788              
1789             =cut
1790              
1791             sub prune {
1792 312     312 1 8349 my ( $class, $self ) = &$invocant_cr;
1793 312     1498   939 my $callback = sub { !defined $_ };
  1498         3682  
1794 312         837 return $class->prune_callback( $self, $callback );
1795             }
1796              
1797             =item B
1798              
1799             Like C, but treats not only undefined values as blank, but also
1800             empty strings.
1801              
1802             =cut
1803              
1804             sub prune_empty {
1805 4     4 1 8233 my ( $class, $self ) = &$invocant_cr;
1806 4 100   28   17 my $callback = sub { not defined $_ or $_ eq q[] };
  28         155  
1807 4         12 return $class->prune_callback( $self, $callback );
1808             }
1809              
1810             =item B
1811              
1812             Like C, but treats not only undefined values as blank, but also
1813             strings that are empty or that consist solely of white space.
1814              
1815             =cut
1816              
1817             sub prune_space {
1818 4     4 1 10448 my ( $class, $self ) = &$invocant_cr;
1819 4 100   32   19 my $callback = sub { not defined $_ or m[\A \s* \z]x };
  32         198  
1820 4         16 return $class->prune_callback( $self, $callback );
1821             }
1822              
1823             =item B)>
1824              
1825             Like C, but calls the for each element, setting $_ to
1826             each element. If the callback code returns true, the value is
1827             considered padding, and is removed if it's beyond the last non-padding
1828             value at the end of a column or row.
1829              
1830             For example, this would prune values that were undefined, the empty
1831             string, or zero:
1832              
1833             my $callback = sub {
1834             ! defined $_ or $_ eq q[] or $_ == 0;
1835             }
1836             $obj->prune_callback($callback);
1837              
1838             In void context, alters the original object. Otherwise, creates a new
1839             Array::2D object and returns the object.
1840              
1841             Completely empty rows cannot be sent to the callback function,
1842             so those are always removed.
1843              
1844             =cut
1845              
1846             sub prune_callback {
1847 324     324 1 10580 my ( $class, $orig ) = &$invocant_cr;
1848 324         527 my $callback = shift;
1849 324         437 my $self;
1850              
1851 324 100       643 if ( defined wantarray ) {
1852 8         27 $self = $class->clone($orig);
1853             }
1854             else {
1855 316         759 $self = $orig;
1856             }
1857              
1858             # remove final blank rows
1859 324   100     425 while (
      100        
1860 454         1572 @{$self}
1861             and ( not defined $self->[-1]
1862             or 0 == @{ $self->[-1] }
1863 430     430   2439 or all { $callback->() } @{ $self->[-1] } )
1864             )
1865             {
1866 130         542 pop @{$self};
  130         223  
1867             }
1868              
1869             # return if it's all blank
1870 324 100       1914 return $self unless ( @{$self} );
  324         792  
1871              
1872             # remove final blank columns
1873              
1874 288         432 foreach my $row_r ( @{$self} ) {
  288         494  
1875 884         1153 while ( @{$row_r} ) {
  1192         2228  
1876 1164         1685 local $_ = $row_r->[-1];
1877 1164 100       1729 last if not $callback->();
1878 308         546 pop @$row_r;
1879             }
1880             }
1881              
1882 288         766 return $self;
1883             } ## tidy end: sub prune_callback
1884              
1885             =item B)>
1886              
1887             The opposite of C, this pads out the array so every column
1888             has the same number of elements. If provided, the added elements are
1889             given the value provided; otherwise, they are set to undef.
1890              
1891             =cut
1892              
1893             sub pad {
1894 8     8 1 18633 my ( $class, $orig ) = &$invocant_cr;
1895 8         19 my $padding = shift;
1896 8         15 my $self;
1897 8 100       19 if ( defined wantarray ) {
1898 4         17 $self = $class->clone($orig);
1899             }
1900             else {
1901 4         7 $self = $orig;
1902             }
1903 8         28 my $last_col = $class->last_col($self);
1904              
1905 8 100       25 if ( not defined $padding ) {
1906 4         34 foreach (@$self) {
1907 12         23 $#{$_} = $last_col;
  12         37  
1908             }
1909             }
1910             else {
1911 4         18 foreach (@$self) {
1912 12         20 push @$_, $padding while $#{$_} < $last_col;
  24         64  
1913             }
1914             }
1915              
1916 8         23 return $self;
1917              
1918             } ## tidy end: sub pad
1919              
1920             =back
1921              
1922             =head2 MODIFYING EACH ELEMENT
1923              
1924             Each of these methods alters the original array in void context.
1925             If not in void context, creates a new Array::2D object and returns
1926             the object.
1927              
1928             =over
1929              
1930             =item B)>
1931              
1932             Calls the C<$code_ref> for each element, aliasing $_ to each element in
1933             turn. This allows an operation to be performed on every element.
1934              
1935             For example, this would lowercase every element in the array (assuming
1936             all values are defined):
1937              
1938             $obj->apply(sub {lc});
1939              
1940             If an entry in the array is undefined, it will still be passed to the
1941             callback.
1942              
1943             For each invocation of the callback, @_ is set to the row and column
1944             indexes (0-based).
1945              
1946             =cut
1947              
1948             sub apply {
1949 130     130 1 21100 my ( $class, $orig ) = &$invocant_cr;
1950 130         209 my $callback = shift;
1951 130         181 my $self;
1952              
1953 130 100       296 if ( defined wantarray ) {
1954 98         239 $self = $class->clone($orig);
1955             }
1956             else {
1957 32         52 $self = $orig;
1958             }
1959              
1960 130         199 for my $row ( @{$self} ) {
  130         255  
1961 382         591 for my $idx ( 0 .. $#{$row} ) {
  382         720  
1962 1100         1710 for ( $row->[$idx] ) {
1963             # localize $_ to $row->[$idx]. Autovivifies the row.
1964 1100         1732 $callback->( $row, $idx );
1965             }
1966             }
1967             }
1968 130         474 return $self;
1969             } ## tidy end: sub apply
1970              
1971             =item B
1972              
1973             Removes white space, if present, from the beginning and end of each
1974             element in the array.
1975              
1976             =cut
1977              
1978             sub trim {
1979 16     16 1 19548 my ( $class, $self ) = &$invocant_cr;
1980              
1981             my $callback = sub {
1982 84 100   84   162 return unless defined;
1983 64         131 s/\A\s+//;
1984 64         140 s/\s+\z//;
1985 64         120 return;
1986 16         45 };
1987              
1988 16         44 return $class->apply( $self, $callback );
1989             }
1990              
1991             =item B
1992              
1993             Removes white space from the end of each element in the array.
1994              
1995             In void context, alters the original object. Otherwise, creates a new
1996             Array::2D object and returns the object.
1997              
1998             =cut
1999              
2000             sub trim_right {
2001 16     16 1 19806 my ( $class, $self ) = &$invocant_cr;
2002              
2003             my $callback = sub {
2004 84 100   84   168 return unless defined;
2005 64         191 s/\s+\z//;
2006 64         117 return;
2007 16         46 };
2008              
2009 16         38 return $class->apply( $self, $callback );
2010             }
2011              
2012             =item B
2013              
2014             Replaces undefined values with the empty string.
2015              
2016             =cut
2017              
2018             sub define {
2019 82     82 1 19819 my ( $class, $self ) = &$invocant_cr;
2020              
2021             my $callback = sub {
2022 848   100 848   1952 $_ //= q[];
2023 82         269 };
2024 82         270 return $class->apply( $self, $callback );
2025             }
2026              
2027             =back
2028              
2029             =head2 TRANSFORMING ARRAYS INTO OTHER STRUCTURES
2030              
2031             =over
2032              
2033             =item B)>
2034              
2035             Returns a hash reference. The values of the specified
2036             column of the array become the keys of the hash. The values of the hash
2037             are arrayrefs containing the elements
2038             of the rows of the array, with the value in the key column removed.
2039              
2040             If the key column is not specified, the first column is used for the
2041             keys.
2042              
2043             So:
2044              
2045             $obj = Array::2D->new([qw/a 1 2/],[qw/b 3 4/]);
2046             $hashref = $obj->hash_of_rows(0);
2047             # $hashref = { a => [ '1' , '2' ] , b => [ '3' , '4' ] }
2048              
2049             =cut
2050              
2051             sub hash_of_rows {
2052 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
2053 0         0 my $col = shift;
2054              
2055 0         0 my %hash;
2056              
2057 0 0       0 if ($col) {
2058 0         0 for my $row_r ( @{$self} ) {
  0         0  
2059 0         0 my @row = @{$row_r};
  0         0  
2060 0         0 my $key = splice( @row, $col, 1 );
2061 0         0 $hash{$key} = \@row;
2062             }
2063             }
2064             else {
2065              
2066 0         0 for my $row_r ( @{$self} ) {
  0         0  
2067 0         0 my @row = @{$row_r};
  0         0  
2068 0         0 my $key = shift @row;
2069 0         0 $hash{$key} = \@row;
2070             }
2071              
2072             }
2073              
2074 0         0 return \%hash;
2075             } ## tidy end: sub hash_of_rows
2076              
2077             =item B)>
2078              
2079             Like C, but accepts a key column and a value column, and
2080             the values are not whole rows but only single elements.
2081              
2082             So:
2083              
2084             $obj = Array::2D->new([qw/a 1 2/],[qw/b 3 4/]);
2085             $hashref = $obj->hash_of_row_elements(0, 1);
2086             # $hashref = { a => '1' , b => '3' }
2087              
2088             If neither key column nor value column are specified, column 0 will be
2089             used for the key and the column 1 will be used for the value.
2090              
2091             If the key column is specified but the value column is not, then the
2092             first column that is not the key column will be used as the value
2093             column. (In other words, if the key column is column 0, then column 1
2094             will be used as the value; otherwise column 0 will be used as the
2095             value.)
2096              
2097             =cut
2098              
2099             sub hash_of_row_elements {
2100 0     0 1 0 my ( $class, $self ) = &$invocant_cr;
2101              
2102 0         0 my ( $keycol, $valuecol );
2103 0 0       0 if (@_) {
2104 0         0 $keycol = shift;
2105 0 0 0     0 $valuecol = shift // ( 0 == $keycol ? 1 : 0 );
2106              
2107             # $valuecol defaults to first column that is not the same as $keycol
2108             }
2109             else {
2110 0         0 $keycol = 0;
2111 0         0 $valuecol = 1;
2112             }
2113              
2114 0         0 my %hash;
2115 0         0 for my $row_r ( @{$self} ) {
  0         0  
2116 0         0 $hash{ $row_r->[$keycol] } = $row_r->[$valuecol];
2117             }
2118              
2119 0         0 return \%hash;
2120             } ## tidy end: sub hash_of_row_elements
2121              
2122             =back
2123              
2124             =head2 TABULATING INTO COLUMNAR OUTPUT
2125              
2126             If the L module can be loaded,
2127             its C method will be used to determine the width of each
2128             character. This will treat composed accented characters and
2129             double-width Asian characters correctly.
2130              
2131             Otherwise, Array::2D will use Perl's C function.
2132              
2133             =over
2134              
2135             =item B)>
2136              
2137             Returns an arrayref of strings, where each string consists of the
2138             elements of each row, padded with enough spaces to ensure that each
2139             column has a consistent width.
2140              
2141             The columns will be separated by whatever string is passed to
2142             C. If nothing is passed, a single space will be used.
2143              
2144             So, for example,
2145              
2146             $obj = Array::2D->new([qw/a bbb cc/],[qw/dddd e f/]);
2147             $arrayref = $obj->tabulate();
2148              
2149             # $arrayref = [ 'a bbb cc' ,
2150             # 'dddd e f'
2151             # ];
2152              
2153             Completely empty columns and rows will be removed.
2154              
2155             =item B)>
2156              
2157             Like C, but instead of each column having its own width,
2158             all columns have the same width.
2159              
2160             =cut
2161              
2162             my $prune_space_list_cr = sub {
2163             my @cells = @_;
2164              
2165             pop @cells
2166             while @cells
2167             and (not defined $cells[-1]
2168             or $cells[-1] eq q[]
2169             or $cells[-1] =~ m/\A\s*\z/ );
2170              
2171             return @cells;
2172             };
2173              
2174             {
2175             my $equal_width;
2176              
2177             my $tabulate_cr = sub {
2178             my ( $class, $orig ) = &$invocant_cr;
2179             my $self = $class->define($orig);
2180              
2181             my $separator = shift // q[ ];
2182             my @length_of_col;
2183             my $maxwidths = 0;
2184              
2185             foreach my $row ( @{$self} ) {
2186             my @cells = @{$row};
2187             for my $this_col ( 0 .. $#cells ) {
2188             my $thislength = $text_columns_cr->( $cells[$this_col] );
2189              
2190             $maxwidths = max( $maxwidths, $thislength ) if $equal_width;
2191             $length_of_col[$this_col] = $thislength
2192             if ( not $length_of_col[$this_col]
2193             or $length_of_col[$this_col] < $thislength );
2194             }
2195             }
2196              
2197             my @lines;
2198              
2199             foreach my $record_r ( @{$self} ) {
2200             my @cells = $prune_space_list_cr->( @{$record_r} );
2201              
2202             # prune trailing cells
2203              
2204             next unless @cells; # skip blank rows
2205              
2206             for my $this_col ( reverse( 0 .. ( $#cells - 1 ) ) ) {
2207             if ( 0 == $length_of_col[$this_col] ) {
2208             splice @cells, $this_col, 1;
2209             next;
2210             }
2211             # delete blank columns so it doesn't add the separator
2212              
2213             my $width
2214             = $equal_width ? $maxwidths : $length_of_col[$this_col];
2215              
2216             #$cells[$this_col]
2217             # = sprintf( '%-*s', $width, $cells[$this_col] );
2218            
2219             my $spaces = $width - $text_columns_cr->( $cells[$this_col]);
2220             $cells[$this_col] .= ( ' ' x $spaces) if $spaces > 0;
2221             }
2222             push @lines, join( $separator, @cells );
2223              
2224             } ## tidy end: foreach my $record_r ( @{$self...})
2225              
2226             return \@lines;
2227              
2228             };
2229              
2230             sub tabulate {
2231 40     40 1 25982 $equal_width = 0;
2232 40         120 goto $tabulate_cr;
2233             }
2234              
2235             sub tabulate_equal_width {
2236 26     26 1 26346 $equal_width = 1;
2237 26         77 goto $tabulate_cr;
2238             }
2239              
2240             }
2241              
2242             =item B)>
2243              
2244             Like C, but returns the data as a single string, using
2245             line feeds as separators of rows, suitable for sending to a terminal.
2246              
2247             =cut
2248              
2249             sub tabulated {
2250 20     20 1 25022 my ( $class, $self ) = &$invocant_cr;
2251 20         78 my $lines_r = $class->tabulate( $self, @_ );
2252 20         102 return join( "\n", @$lines_r ) . "\n";
2253             }
2254              
2255             =back
2256              
2257             =head2 SERIALIZING AND OUTPUT TO FILES
2258              
2259             =over
2260              
2261             =item B<< tsv_lines(I) >>
2262              
2263             Returns a list of strings in list context, or an arrayref of strings in
2264             scalar context. The elements of each row are present in the string,
2265             separated by tab characters.
2266              
2267             If there are any arguments, they will be used first as the first
2268             row of text. The idea is that these will be the headers of the
2269             columns. It's not really any different than putting the column
2270             headers as the first element of the data, but frequently these are
2271             stored separately. If there is only one element and it is a reference
2272             to an array, that array will be used as the first row of text.
2273              
2274             If tabs are present in any element,
2275             they will be replaced by the Unicode Replacement Character, U+FFFD.
2276              
2277             =cut
2278              
2279             =item B<< tsv(I) >>
2280              
2281             Returns a single string with the elements of each row delimited by
2282             tabs, and rows delimited by line feeds.
2283              
2284             If there are any arguments, they will be used first as the first
2285             row of text. The idea is that these will be the headers of the
2286             columns. It's not really any different than putting the column
2287             headers as the first element of the data, but frequently these are
2288             stored separately. If there is only one element and it is a reference
2289             to an array, that array will be used as the first row of text.
2290              
2291             If tabs or line feeds are present in any element,
2292             they will be replaced by the Unicode Replacement Character, U+FFFD.
2293              
2294             =cut
2295              
2296             sub tsv_lines {
2297              
2298 70     70 1 50712 my ( $class, $self ) = &$invocant_cr;
2299 70         181 my @rows = @$self;
2300              
2301 70         135 my @lines;
2302              
2303 70         159 my @headers = @_;
2304 70 100       230 if (@headers) {
2305 34 50 33     139 if ( 1 == @headers and is_plain_arrayref( $headers[0] ) ) {
2306 0         0 unshift @rows, $headers[0];
2307             }
2308             else {
2309 34         90 unshift @rows, \@headers;
2310             }
2311             }
2312              
2313 70         137 my $carped;
2314 70         150 foreach my $row (@rows) {
2315 224         349 my @cells = @{$row};
  224         512  
2316 224         442 foreach (@cells) {
2317 712   100     1585 $_ //= q[];
2318 712         1309 my $substitutions = s/\t/\x{FFFD}/g;
2319 712 100 100     1873 if ( $substitutions and not $carped ) {
2320 12         1524 carp 'Tab character found converting to tab-separated values. '
2321             . 'Replaced with REPLACEMENT CHARACTER';
2322 12         1163 $carped = 1;
2323             }
2324             }
2325              
2326 224         502 @cells = $prune_space_list_cr->(@cells);
2327              
2328 224         653 my $line = join( "\t", @cells );
2329 224         557 push @lines, $line;
2330             }
2331              
2332 70 100       370 return wantarray ? @lines : \@lines;
2333              
2334             } ## tidy end: sub tsv_lines
2335              
2336             sub tsv {
2337              
2338             # tab-separated-values,
2339             # suitable for something like File::Slurper::write_text
2340              
2341             # converts line feeds, tabs, and carriage returns to the Replacement
2342             # Character.
2343              
2344 38     38 1 48278 my ( $class, $self ) = &$invocant_cr;
2345              
2346 38         117 my $lines_r = $class->tsv_lines( $self, @_ );
2347              
2348 38         69 my $carped;
2349 38         76 foreach my $line (@$lines_r) {
2350 118         222 my $substitutions = $line =~ s/\n/\x{FFFD}/g;
2351 118 100 66     311 if ( $substitutions and not $carped ) {
2352 6         519 carp 'Line feed character found assembling tab-separated values. '
2353             . 'Replaced with REPLACEMENT CHARACTER';
2354 6         643 $carped = 1;
2355             }
2356             }
2357 38         211 return join( "\n", @$lines_r ) . "\n";
2358              
2359             } ## tidy end: sub tsv
2360              
2361             =item B<< file(...) >>
2362              
2363             Accepts a file specification and creates a new file at that location
2364             containing the data in the 2D array.
2365              
2366             This method uses named parameters.
2367              
2368             =over
2369              
2370             =item type
2371              
2372             This parameter is the file's type. Currently, the types recognized are
2373             'tsv' for tab-separated values, and 'xlsx' for Excel XLSX. If the type
2374             is not given, it attempts to determine the type from the file
2375             extension, which can be (case-insensitively) 'xlsx' for Excel XLSX
2376             files or 'tab', 'tsv' or 'txt' for tab-separated value files.
2377              
2378             (If other text file formats are someday added, either they will have
2379             to have different extensions, or an explicit type must be passed
2380             to force that type to have a ".txt" extension.
2381              
2382             =item output_file
2383              
2384             This mandatory parameter contains the file specification.
2385              
2386             =item headers
2387              
2388             This parameter is optional. If present, it contains an array reference
2389             to be used as the first row in the ouptut file.
2390              
2391             The idea is that these will be the headers of the columns. It's not
2392             really any different than putting the column headers as the first
2393             element of the data, but frequently these are stored separately.
2394              
2395             =back
2396              
2397             =cut
2398              
2399             sub file {
2400 0     0 1   my ( $class, $self ) = &$invocant_cr;
2401              
2402 0           my %params = validate(
2403             @_,
2404             { headers => { type => ARRAYREF, optional => 1 },
2405             output_file => 1,
2406             type => 0,
2407             },
2408             );
2409 0           my $output_file = $params{output_file};
2410 0   0       my $type = $params{type} || $filetype_from_ext_r->($output_file);
2411              
2412 0 0         croak "Cannot determine type of $output_file in " . __PACKAGE__ . '->file'
2413             unless $type;
2414              
2415 0 0         if ( $type eq 'xlsx' ) {
2416 0           $class->xlsx( $self, \%params );
2417 0           return;
2418             }
2419 0 0         if ( $type eq 'tsv' ) {
2420 0           my $text = $class->tsv($self);
2421              
2422 0 0         if ( $params{headers} ) {
2423 0           $text = join( "\t", @{ $params{headers} } ) . "\n" . $text;
  0            
2424             }
2425              
2426 0           require File::Slurper;
2427 0           File::Slurper::write_text( $output_file, $text );
2428 0           return;
2429             }
2430 0           croak "Unrecognized type $type in " . __PACKAGE__ . '->file';
2431             } ## tidy end: sub file
2432              
2433             =item B<< xlsx(...) >>
2434              
2435             Accepts a file specification and creates a new Excel XLSX file at that
2436             location, with one sheet, containing the data in the 2D array.
2437              
2438             This method uses named parameters.
2439              
2440             =over
2441              
2442             =item output_file
2443              
2444             This mandatory parameter contains the file specification.
2445              
2446             =item headers
2447              
2448             This parameter is optional. If present, it contains an array reference
2449             to be used as the first row in the Excel file.
2450              
2451             The idea is that these will be the headers of the columns. It's not
2452             really any different than putting the column headers as the first
2453             element of the data, but frequently these are stored separately. At
2454             this point no attempt is made to make them bold or anything like that.
2455              
2456             =item format
2457              
2458             This parameter is optional. If present, it contains a hash reference,
2459             with format parameters as specified by Excel::Writer::XLSX.
2460              
2461             =back
2462              
2463             =cut
2464              
2465             sub xlsx {
2466 0     0 1   my ( $class, $self ) = &$invocant_cr;
2467 0           my %params = validate(
2468             @_,
2469             { headers => { type => ARRAYREF, optional => 1 },
2470             format => { type => HASHREF, optional => 1 },
2471             output_file => 1,
2472             },
2473             );
2474              
2475 0           my $output_file = $params{output_file};
2476 0           my $format_properties = $params{format};
2477 0           my @headers;
2478 0 0         if ( $params{headers} ) {
2479 0           @headers = @{ $params{headers} };
  0            
2480             }
2481              
2482 0           require Excel::Writer::XLSX; ### DEP ###
2483              
2484 0           my $workbook = Excel::Writer::XLSX->new($output_file);
2485             ## no critic (Variables::ProhibitPunctuationVars]
2486 0 0         croak "Can't open $output_file for writing: $!"
2487             unless defined $workbook;
2488             ## use critic
2489 0           my $sheet = $workbook->add_worksheet();
2490 0           my @format;
2491              
2492 0 0         if ( defined $format_properties ) {
2493 0           push @format, $workbook->add_format(%$format_properties);
2494             }
2495              
2496             # an array @format is used because if it were a scalar, it would be undef,
2497             # where what we want if it is empty is no value at all
2498              
2499 0 0         my $unblessed = blessed $self ? $self->unblessed : $self;
2500              
2501             # Excel::Writer::XLSX checks 'ref' and not 'reftype'
2502              
2503 0 0         if (@headers) {
2504 0           $sheet->write_row( 0, 0, \@headers, @format );
2505 0           $sheet->write_col( 1, 0, $unblessed, @format );
2506             }
2507             else {
2508 0           $sheet->write_col( 0, 0, $unblessed, @format );
2509             }
2510              
2511 0           return $workbook->close();
2512              
2513             } ## tidy end: sub xlsx
2514              
2515             1;
2516              
2517             __END__