Skip Menu |

This queue is for tickets about the List-UtilsBy CPAN distribution.

Report information
The Basics
Id: 120194
Status: open
Priority: 0/
Queue: List-UtilsBy

People
Owner: Nobody in particular
Requestors: andy [...] petdance.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Add sum_by
Right now if I want to add up everyone's salary I have to my $payroll = sum map { $_->salary } @employees; when it would be nice to be able to say my $payroll = sum_by { $_->salary } @employees; I know it's not that big of a savings, but for being orthoganol to List::Util.
From: andy [...] petdance.com
Here's code for sum_by and sum0_by.
Subject: sum_by.patch
diff --git a/Changes b/Changes index 1b80d16..5b68b44 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,9 @@ Revision history for List-UtilsBy +NEXT + [CHANGES] + * Added 'sum_by' and 'sum0_by' + 0.10 2015/07/16 19:22:00 [CHANGES] * Added 'extract_first_by' diff --git a/MANIFEST b/MANIFEST index 1e78fde..c3ebef4 100644 --- a/MANIFEST +++ b/MANIFEST @@ -20,5 +20,6 @@ t/09unzip_by.t t/10extract_by.t t/11weighted_shuffle_by.t t/12bundle_by.t +t/13sum_by.t t/99pod.t t/Unrandom.pm diff --git a/lib/List/UtilsBy.pm b/lib/List/UtilsBy.pm index 0ff59f9..827758a 100644 --- a/lib/List/UtilsBy.pm +++ b/lib/List/UtilsBy.pm @@ -1,7 +1,7 @@ # You may distribute under the terms of either the GNU General Public License # or the Artistic License (the same terms as Perl itself) # -# (C) Paul Evans, 2009-2015 -- leonerd@leonerd.org.uk +# (C) Paul Evans, 2009-2017 -- leonerd@leonerd.org.uk package List::UtilsBy; @@ -21,6 +21,8 @@ our @EXPORT_OK = qw( max_by nmax_by min_by nmin_by + sum_by sum0_by + uniq_by partition_by @@ -255,6 +257,64 @@ sub min_by(&@) *nmin_by = \&min_by; +=head2 sum_by + +=head2 sum0_by + + $sum = sum_by { VALUEFUNC } @vals; + + $sum = sum0_by { VALUEFUNC } @vals; + +Returns the sum of the results of VALUEFUNC applied to each of the values +in C<@vals>. + +For example: + + $total_salary = sum_by { $_->salary } @employees; + +This is the same as using C<sum> from L<List::Util> like + + $total_salary = sum map { $_->salary } @employees; + +but without the intermediate results of the C<map>. + +If called on an empty list, C<sum_by> returns undef, and C<sum0_by> +returns 0. + +=cut + +sub sum_by(&@) +{ + my $code = shift; + + return undef unless @_; + + local $_; + + my $sum = 0; + + foreach ( @_ ) { + $sum += $code->(); + } + + return $sum; +} + +sub sum0_by(&@) +{ + my $code = shift; + + local $_; + + my $sum = 0; + + foreach ( @_ ) { + $sum += $code->(); + } + + return $sum; +} + =head2 uniq_by @vals = uniq_by { KEYFUNC } @vals diff --git a/t/13sum_by.t b/t/13sum_by.t new file mode 100644 index 0000000..b39feff --- /dev/null +++ b/t/13sum_by.t @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; + +use List::UtilsBy qw( sum_by sum0_by ); + +my @employees = ( + Employee->new( 50_000 ), + Employee->new( 90_000 ), + Employee->new( 75_000 ), +); + +SUM: { + my $sum; + $sum = sum_by { $_ * 9 } 1..10; + is( $sum, 495, 'Mulitplication on sum_by' ); + + $sum = sum_by { $_ * $_ } (); + is( $sum, undef, 'Empty list = undef' ); + + $sum = sum_by { $_->salary } @employees; + is( $sum, 215_000, 'Salary sum' ); +} + + +SUM0: { + my $sum0; + $sum0 = sum0_by { $_ * 9 } 1..10; + is( $sum0, 495, 'Mulitplication on sum0_by' ); + + $sum0 = sum0_by { $_ * $_ } (); + is( $sum0, 0, 'Empty list = undef' ); + + $sum0 = sum0_by { $_->salary } @employees; + is( $sum0, 215_000, 'Salary sum0' ); +} + +done_testing; + +exit 0; + +package Employee; + +sub new { + my $class = shift; + my $salary = shift; + + return bless { salary => $salary }, $class; +} + +sub salary { + my $self = shift; + + return $self->{salary}; +}
I've just remembered why I decided not to put these in initially. They're actually quite easy to implement efficiently on core's List::Util::reduce. This is even mentioned in the List::Util docs: https://metacpan.org/pod/List::Util#reduce The above example code blocks also suggest how to use reduce to build a more efficient combined version of one of these basic functions and a map block. For example, to find the total length of the all the strings in a list, we could use $total = sum map { length } @strings; However, this produces a list of temporary integer values as long as the original list of strings, only to reduce it down to a single value again. We can compute the same result more efficiently by using reduce with a code block that accumulates lengths by writing this instead as: $total = reduce { $a + length $b } 0, @strings -- Paul Evans
Subject: Re: [rt.cpan.org #120194] Add sum_by
Date: Fri, 24 Feb 2017 19:49:31 -0600
To: bug-List-UtilsBy [...] rt.cpan.org
From: Andy Lester <andy [...] petdance.com>
Show quoted text
> On Feb 24, 2017, at 6:40 PM, Paul Evans via RT <bug-List-UtilsBy@rt.cpan.org> wrote: > > I've just remembered why I decided not to put these in initially.
Are you saying you don't want to put them in? To me $total = reduce { $a + length $b } 0, @strings is not nearly as immediately obvious of what is happening as either of $total = sum_by { length } @strings; or $total = sum map { length } @strings; -- Andy Lester => www.petdance.com
I also think that a sum_by would be a great addition. Even a reduce_by here would help, I think. On Fri Feb 24 20:49:52 2017, andy@petdance.com wrote: Show quoted text
>
> > On Feb 24, 2017, at 6:40 PM, Paul Evans via RT <bug-List- > > UtilsBy@rt.cpan.org> wrote: > > > > I've just remembered why I decided not to put these in initially.
> > Are you saying you don't want to put them in? > > To me > > $total = reduce { $a + length $b } 0, @strings > > is not nearly as immediately obvious of what is happening as either of > > $total = sum_by { length } @strings; > > or > > $total = sum map { length } @strings; > > -- > Andy Lester => www.petdance.com