aboutsummaryrefslogtreecommitdiff
path: root/lib/List/Breakdown.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/List/Breakdown.pm')
-rw-r--r--lib/List/Breakdown.pm221
1 files changed, 221 insertions, 0 deletions
diff --git a/lib/List/Breakdown.pm b/lib/List/Breakdown.pm
new file mode 100644
index 0000000..44efc58
--- /dev/null
+++ b/lib/List/Breakdown.pm
@@ -0,0 +1,221 @@
+package List::Breakdown;
+
+# Force me to write this properly
+use strict;
+use warnings;
+use utf8;
+
+# Target reasonably old Perls
+use 5.006;
+
+# Import required modules
+use Carp;
+
+# Handle exporting in a way Perl v5.6 should tolerate
+use base qw(Exporter);
+our @EXPORT_OK = 'breakdown';
+
+# Specify package version
+our $VERSION = 0.06;
+
+# Dispatch table of functions to handle different ref types for the spec
+# hashref's values
+my %types = (
+
+ # If it's a hash, apply breakdown() again as if it were another root-level
+ # spec
+ HASH => sub {
+ my $spec = shift;
+ return { breakdown( $spec, @_ ) };
+ },
+
+ # If it's a subroutine, return a arrayref of all elements for which it
+ # returns true
+ CODE => sub {
+ my $sub = shift;
+ return [ grep { $sub->() } @_ ];
+ },
+
+ # If it's a regular expression, return an arrayref of all elements it
+ # matches
+ Regexp => sub {
+ my $re = shift;
+ return [ grep { $_ =~ $re } @_ ];
+ },
+);
+
+# Given a spec and a list of items, filter them into a hash of the same
+# structure
+sub breakdown {
+ my ( $spec, @items ) = @_;
+
+ # Check the spec is a hashref
+ ref $spec eq 'HASH'
+ or croak 'HASH reference expected for first argument';
+
+ # Start building a results hash
+ my %results;
+ for my $key ( keys %{$spec} ) {
+
+ # Check that the value for this key is a reference
+ my $ref = ref $spec->{$key}
+ or croak "Reference expected for '$key'";
+
+ # Check it's a reference we understand
+ exists $types{$ref}
+ or croak "Unhandled ref type $ref for '$key'";
+
+ # Apply the appropriate subroutine for this reference type to the list
+ # of items
+ $results{$key} = $types{$ref}->( $spec->{$key}, @items );
+ }
+
+ # Return the constructed result set
+ return %results;
+}
+
+1;
+
+__END__
+
+=pod
+
+=for stopwords
+sublists Unhandled tradename licensable MERCHANTABILITY
+
+=head1 NAME
+
+List::Breakdown - Build list sublists matching conditions
+
+=head1 VERSION
+
+Version 0.06
+
+=head1 DESCRIPTION
+
+This module "breaks down" a list--filtering elements from a list into a
+specified hash structure, which can be nested. It may be useful in situations
+where you have a big list of things to generate reports on or to otherwise
+filter into several sublists.
+
+It differs from the excellent C<List::Categorize> in the use of subroutine
+references for each category and in not requiring only one final category for
+any given item; an item can end up in the result set for more than one filter.
+
+You could maybe think of this as a multi-C<grep> that returns named results.
+
+=head1 SYNOPSIS
+
+ my @words = qw(foo bar baz quux wibble florb);
+ my $cats = {
+ all => sub { 1 },
+ has_b => sub { m/ b /msx },
+ has_w => sub { m/ w /msx },
+ length => {
+ 3 => sub { length == 3 },
+ 4 => sub { length == 4 },
+ long => sub { length > 4 },
+ },
+ has_ba => qr/ba/msx,
+ };
+ my %filtered = breakdown $cats, @words;
+
+=head1 SUBROUTINES/METHODS
+
+=head2 B<breakdown(\%spec, @items)>
+
+Given a hash reference structure and a list of items, apply each of the
+subroutines or regular expressions given as values of the hash reference,
+returning a new hash in the same structure with the tests replaced with the
+items for which the subroutine returns true, in the same way as C<grep>, or (as
+a shortcut) for which the regular expression matched.
+
+=head1 AUTHOR
+
+Tom Ryder C<< <tom@staff.inspire.net.nz> >>
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item HASH reference expected for first argument
+
+The first argument that B<breakdown()> saw wasn't the hash reference it expects.
+That's the only format a spec is allowed to have.
+
+=item Reference expected for '%s'
+
+The value for the named key in the spec was not a reference, and one was
+expected.
+
+=item Unhandled ref type %s for '%s'
+
+The value for the named key in the spec is of a type that makes no sense to
+this module. Legal reference types are C<HASH>, C<CODE>, and C<Regexp>.
+
+=back
+
+=head1 DEPENDENCIES
+
+Perl 5.6 and the core modules C<Carp> and C<Exporter>.
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+None required.
+
+=head1 INCOMPATIBILITIES
+
+None known.
+
+=head1 BUGS AND LIMITATIONS
+
+Definitely. This is a very early release. Please report any bugs or feature
+requests to C<tom@staff.inspire.net.nz>.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the C<perldoc> command.
+
+ perldoc List::Breakdown
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright (C) 2017 Tom Ryder
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the the Artistic License (2.0). You may obtain a copy of the full
+license at:
+
+<http://www.perlfoundation.org/artistic_license_2_0>
+
+Any use, modification, and distribution of the Standard or Modified Versions is
+governed by this Artistic License. By using, modifying or distributing the
+Package, you accept this license. Do not use, modify, or distribute the
+Package, if you do not accept this license.
+
+If your Modified Version has been derived from a Modified Version made by
+someone other than you, you are nevertheless required to ensure that your
+Modified Version complies with the requirements of this license.
+
+This license does not grant you the right to use any trademark, service mark,
+tradename, or logo of the Copyright Holder.
+
+This license includes the non-exclusive, worldwide, free-of-charge patent
+license to make, have made, use, offer to sell, sell, import and otherwise
+transfer the Package with respect to any patent claims licensable by the
+Copyright Holder that are necessarily infringed by the Package. If you
+institute patent litigation (including a cross-claim or counterclaim) against
+any party alleging that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the date
+that such litigation is filed.
+
+Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
+CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW.
+UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY
+OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+=cut