diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2017-10-06 11:09:24 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2017-10-06 11:36:02 +1300 |
commit | 7fa252fe32a34065852e73aebc00272833f5537a (patch) | |
tree | f302efa68ea8df12a67ff555cf096946a75fd22d /lib | |
parent | Bump version number (diff) | |
download | List-Breakdown-7fa252fe32a34065852e73aebc00272833f5537a.tar.gz List-Breakdown-7fa252fe32a34065852e73aebc00272833f5537a.zip |
Add numeric ranges checking
Diffstat (limited to 'lib')
-rw-r--r-- | lib/List/Breakdown.pm | 74 |
1 files changed, 70 insertions, 4 deletions
diff --git a/lib/List/Breakdown.pm b/lib/List/Breakdown.pm index 8feafac..766098f 100644 --- a/lib/List/Breakdown.pm +++ b/lib/List/Breakdown.pm @@ -30,6 +30,16 @@ my %types = ( return { breakdown( $spec, @_ ) }; }, + # If it's an array, we're doing numeric bounds checking [a,b) + ARRAY => sub { + my $bounds = shift; + @{$bounds} == 2 + or croak 'ARRAY ref for bounds needs two items'; + my $l = defined $bounds->[0] ? $bounds->[0] : '-Inf'; + my $u = defined $bounds->[1] ? $bounds->[1] : 'Inf'; + return [ grep { $_ >= $l and $_ < $u } @_ ]; + }, + # If it's a subroutine, return a arrayref of all elements for which it # returns true CODE => sub { @@ -52,7 +62,7 @@ sub breakdown { # Check the spec is a hashref ref $spec eq 'HASH' - or croak 'HASH reference expected for first argument'; + or croak 'HASH ref expected for first argument'; # Start building a results hash my %results; @@ -60,7 +70,7 @@ sub breakdown { # Check that the value for this key is a reference my $ref = ref $spec->{$key} - or croak "Reference expected for '$key'"; + or croak "Ref expected for '$key'"; # Check it's a reference we understand exists $types{$ref} @@ -83,6 +93,7 @@ __END__ =for stopwords sublists Unhandled tradename licensable MERCHANTABILITY hashrefs CPAN AnnoCPAN +syntaxes =head1 NAME @@ -142,8 +153,24 @@ This puts the following structure in C<%filtered>: 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. +items for which the subroutine returns true, in the same way as C<grep>. + +There are two shortcut syntaxes: + +=over 4 + +=item * + +If a value in the C<spec> structure is an C<ARRAY> reference with two items, it +will be interpreted as defining bounds C<[lower,upper)> for matched values. +`undef` can be used to denote negative or positive infinity. + +=item * + +If it's a C<Regexp> reference, it will be interpreted as a pattern to match +against all of the items, and will return the items that match. + +=back =head1 EXAMPLES @@ -274,6 +301,45 @@ C<%results>: Note the extra level of hash referencing beneath the C<problem> key. +=head2 Grouping numbers by size + +Suppose you have a list of stray numbers from your volcanic activity reporting +system, some of which might be merely worrisome and some an emergency, and they +need to be filtered to know where to send them: + + my @numbers = ( 1, 32, 3718.4, 0x56, 0777, 3.14, -5, 1.2e5 ); + +You could filter them into buckets like this, using the interval syntax; an +array reference with exactly two elements; lower bound (inclusive) first, upper +bound (exclusive) second: + + my $filters = { + negative => [ undef, 0 ], + positive => { + small => [ 0, 10 ], + medium => [ 10, 100 ], + large => [ 100, undef ], + }, + }; + +Applying the bucket structure like so: + + my %filtered = breakdown $filters, @numbers; + +The result set would look like this: + + my %expected = ( + negative => [ -5 ] + positive => { + small => [ 1, 3.14 ], + medium => [ 32, 86 ], + large => [ 3_718.4, 511, 120_000 ] + }, + ); + +Notice that you can express infinity or negative infinity as C<undef>. Note +also this is a numeric comparison only. + =head1 AUTHOR Tom Ryder C<< <tom@sanctum.geek.nz> >> |