Izzy_Murph has asked for the wisdom of the Perl Monks concerning the following question:

Good afternoon!

I'm a researcher in the Humanities and I'm trying my hand at a bit of programming to aid in the analysis of a large dataset. I've got as far as storing my data in a hash of hashes. They keys are the pseudonyms of my participants and the values are the dates (in YYYYMM format) that they attended my programme, and then I also have how many times they attended each month (but this isn't relevant to my question today). What I want to do is, for each participant, print out the earliest and latest date that they attended.

I found some code on StackExchange (reproduced below) which worked okay for the example given on that site, but I think I have misunderstood something because it doesn't produce the expected output using an extract from my dataset. Included below is the code I'm using, the actual output, and the expect output. I would be very grateful for any pointers about what I've done wrong here. Thanks very much in advance.

my %counts = ( "Adam" => { "201708" => 1, "201703" => 1, "201804" => 1, " +201603" => 1, "201705" => 1, "201702" => 1, "201608" => 1, "201704" = +> 1,}, "Sam" => { "201803" => 1, "201801" => 1 }, ); use List::UtilsBy 'max_by'; foreach my $key ( keys %counts ) { my $subhash = $counts{$key}; my $maximal = max_by { $subhash->{$_} } keys %$subhash; print "$key, $maximal\n"; } use List::UtilsBy 'min_by'; foreach my $key ( keys %counts ) { my $subhash = $counts{$key}; my $minimal = max_by { $subhash->{$_} } keys %$subhash; print "$key, $minimal\n"; }
------------ OUTPUT Adam, 201704 Sam, 201801 Adam, 201704 Sam, 201801 ------------- EXPECTED OUTPUT Adam, 201804 Sam, 201803 Adam, 201603 Sam, 201801

Replies are listed 'Best First'.
Re: Using List::UtilsBy to print max and min values from a hash of hashes
by choroba (Cardinal) on Apr 13, 2020 at 16:18 UTC
    By specifying max_by { $subhash->{$_} }, you're sorting by values, i.e. by the number 1 in the subhashes. That's not what you want. You want to sort directly by $_.

    Also, you used max_by even in the second paragraph where you wanted min_by.

    After fixing these, it works as you expected:

    #! /usr/bin/perl use warnings; use strict; use List::UtilsBy qw{ min_by max_by }; my %counts = ( Adam => { "201708" => 1, "201703" => 1, "201804" => 1, "201603" => 1, "201705" => 1, "201702" => 1, "201608" => 1, "201704" => 1, }, Sam => { "201803" => 1, "201801" => 1 }, ); for my $name ( keys %counts ) { my $subhash = $counts{$name}; my $maximal = max_by { $_ } keys %$subhash; print "$name, $maximal\n"; } for my $name ( keys %counts ) { my $subhash = $counts{$name}; my $minimal = min_by { $_ } keys %$subhash; print "$name, $minimal\n"; }

    Update:

    I also moved the use statement to the top (as it's executed during the compilation phase, anyway) and replaced foreach with its shorter form.

    Note that you might use the minmax_by function, too:

    use List::UtilsBy qw{ minmax_by }; for my $name ( keys %counts ) { my $subhash = $counts{$name}; my ($minimal, $maximal) = minmax_by { $_ } keys %$subhash; print "$name, $minimal - $maximal\n"; }

    Update 2: But using { $_ } means you can get back to List::Util::min or max, or List::MoreUtils::minmax.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      Ah, brilliant! Thank you so much for this.
Re: Using List::UtilsBy to print max and min values from a hash of hashes
by soonix (Chancellor) on Apr 13, 2020 at 16:19 UTC
    You ask max_by for the maximum of the subhashes' values ($subhash->{$_} will return the value, in your case always "1"). A simple List::Util::max should suffice.