Skip to content

New option group_rows_by #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ some basic capabilities for configuring the appearance of the tables.
You can read a nicely formatted version of the documentation for
this module online:

https://metacpan.org/pod/Module::Info
https://metacpan.org/pod/Text::Table::Tiny


You should be able to install this using your usual method for installing
Expand Down
89 changes: 85 additions & 4 deletions lib/Text/Table/Tiny.pm
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,31 @@ sub generate_table {
$format,
map { defined($header_row->[$_]) ? $header_row->[$_] : '' } (0..$max_index)
);
push @table, $params{separate_rows} ? $head_row_sep : $row_sep;
push @table, $params{separate_rows} || $params{group_rows_by} ? $head_row_sep : $row_sep;
}

# then the data
foreach my $row ( @{ $rows }[$data_begins..$#$rows] ) {
foreach my $row_num ( $data_begins .. $#$rows ) {
my $row = $rows->[$row_num];

push @table, sprintf(
$format,
map { defined($row->[$_]) ? $row->[$_] : '' } (0..$max_index)
);
push @table, $row_sep if $params{separate_rows};

my $next_row = $row_num < $#$rows ? $rows->[$row_num + 1] : undef;
my $is_last_in_rowgroup = $params{group_rows_by}
&& not _is_same_colgroup($row, $next_row, $params{group_rows_by});
if ($params{separate_rows}) {
push @table, $is_last_in_rowgroup ? $head_row_sep : $row_sep;
}
elsif ($is_last_in_rowgroup) {
push @table, $row_sep;
}
}

# this will have already done the bottom if called explicitly
push @table, $row_sep unless $params{separate_rows};
push @table, $row_sep unless $params{separate_rows} || $params{group_rows_by};
return join("\n",grep {$_} @table);
}

Expand Down Expand Up @@ -100,6 +111,17 @@ sub _get_header_row_separator {
return "$HEADER_CORNER_MARKER$HEADER_ROW_SEPARATOR".join("$HEADER_ROW_SEPARATOR$HEADER_CORNER_MARKER$HEADER_ROW_SEPARATOR",map { $HEADER_ROW_SEPARATOR x $_ } @$widths)."$HEADER_ROW_SEPARATOR$HEADER_CORNER_MARKER";
}

sub _is_same_colgroup {
my ($curr_row, $next_row, $col_spec) = @_;

return unless $curr_row && $next_row;

my @curr_columns = map { defined $_ ? $_ : "" } @$curr_row[@$col_spec];
my @next_columns = map { defined $_ ? $_ : "" } @$next_row[@$col_spec];

return "@curr_columns" eq "@next_columns";
}

# Back-compat: 'table' is an alias for 'generate_table', but isn't exported
*table = \&generate_table;

Expand Down Expand Up @@ -216,6 +238,65 @@ You get the maximally ornate:
| carol | brig gen | 8745 |
+-------+----------+----------+

If you want to group consecutive rows which have same values in certain columns:

Assuming the following data:

my $rows = [
# header row
['Name', 'Rank', 'Serial'],
# rows
['alice', 'pvt', '123456'],
['alison', 'pvt', '123457'],
['bob', 'cpl', '98765321'],
['carol', 'brig gen', '8745'],
['darla', 'brig gen', '9745'],
];
print generate_table(rows => $rows, header_row => 1, group_rows_by => [1]);

This will group all consecutive rows with the same Rank (column 1) value.

+--------+----------+----------+
| Name | Rank | Serial |
O========O==========O==========O
| alice | pvt | 123456 |
| alison | pvt | 123457 |
+--------+----------+----------+
| bob | cpl | 98765321 |
+--------+----------+----------+
| carol | brig gen | 8745 |
| darla | brig gen | 8745 |
+--------+----------+----------+

B<NOTE>: The C<group_rows_by> option does *not* sort the rows to keep
rows with same column values together. The input list of rows is
assumed to be already ordered.

When both C<separate_rows> option and C<group_rows_by> option are
specified, the more ornate separator is used for separating rowgroups,
and the ordinary row separator is used for separating rows within the
same group.

print generate_table(rows => $rows, header_row => 1,
separate_rows => 1
group_rows_by => [1]);

This will produce the following output:

+--------+----------+----------+
| Name | Rank | Serial |
O========O==========O==========O
| alice | pvt | 123456 |
+--------+----------+----------+
| alison | pvt | 123457 |
O========O==========O==========O
| bob | cpl | 98765321 |
O========O==========O==========O
| carol | brig gen | 8745 |
+--------+----------+----------+
| darla | brig gen | 9745 |
O========O==========O==========O

=head1 FORMAT VARIABLES

You can set a number of package variables inside the C<Text::Table::Tiny> package
Expand Down
107 changes: 107 additions & 0 deletions t/03-group-rows.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use strict;
use Test::More tests => 5;
use Text::Table::Tiny qw/ generate_table /;

my $rows = [
[ 'Group', 'Member' ],
[ 'Elvis', 'Priscilla' ],
[ 'Elvis', 'Lisa Marie' ],
[ 'Liquor', 'Beer' ],
[ 'Liquor', 'Wine' ],
[ 'Liquor', 'Brandy' ],
[ 'Liquor', 'Rum' ],
[ undef, "That's showbiz!" ],
];

my $t0 = generate_table( rows => $rows );
# print $t0, $/;
is($t0, q%+--------+-----------------+
| Group | Member |
| Elvis | Priscilla |
| Elvis | Lisa Marie |
| Liquor | Beer |
| Liquor | Wine |
| Liquor | Brandy |
| Liquor | Rum |
| | That's showbiz! |
+--------+-----------------+%,
'just rows'
);

my $t1 = generate_table( rows => $rows, header_row => 1 );
# print $t1, $/;
is($t1, q%+--------+-----------------+
| Group | Member |
+--------+-----------------+
| Elvis | Priscilla |
| Elvis | Lisa Marie |
| Liquor | Beer |
| Liquor | Wine |
| Liquor | Brandy |
| Liquor | Rum |
| | That's showbiz! |
+--------+-----------------+%,
'rows and header row'
);

my $t2 = generate_table( rows => $rows, header_row => 1, group_rows_by => [0] );
# print $t2, $/;
is($t2, q%+--------+-----------------+
| Group | Member |
O========O=================O
| Elvis | Priscilla |
| Elvis | Lisa Marie |
+--------+-----------------+
| Liquor | Beer |
| Liquor | Wine |
| Liquor | Brandy |
| Liquor | Rum |
+--------+-----------------+
| | That's showbiz! |
+--------+-----------------+%,
'header row and rows grouped by first column'
);

my $t3 = generate_table( rows => $rows, header_row => 1, group_rows_by => [1] );
# print $t3, $/;
is($t3, q%+--------+-----------------+
| Group | Member |
O========O=================O
| Elvis | Priscilla |
+--------+-----------------+
| Elvis | Lisa Marie |
+--------+-----------------+
| Liquor | Beer |
+--------+-----------------+
| Liquor | Wine |
+--------+-----------------+
| Liquor | Brandy |
+--------+-----------------+
| Liquor | Rum |
+--------+-----------------+
| | That's showbiz! |
+--------+-----------------+%,
'header row and rows grouped by non-first column'
);

my $t4 = generate_table( rows => $rows, header_row => 1, separate_rows => 1, group_rows_by => [0] );
# print $t4, $/;
is($t4, q%+--------+-----------------+
| Group | Member |
O========O=================O
| Elvis | Priscilla |
+--------+-----------------+
| Elvis | Lisa Marie |
O========O=================O
| Liquor | Beer |
+--------+-----------------+
| Liquor | Wine |
+--------+-----------------+
| Liquor | Brandy |
+--------+-----------------+
| Liquor | Rum |
O========O=================O
| | That's showbiz! |
O========O=================O%,
'header row, rows grouped by first column, and separate rows'
);