Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Re: using ref to hash of hash effectively

by alexander_lunev (Pilgrim)
on Dec 27, 2020 at 07:21 UTC ( [id://11125780]=note: print w/replies, xml ) Need Help??


in reply to using ref to hash of hash effectively

Hello!

You already has been provided with more than one way to do this, and I want to participate in this game too. Here's another way - just like you wanted - using map:

use strict; my $weapons_ref = { dagger => { cost => 8, damage => 4, armor => 0 }, shortsword => { cost => 10, damage => 5, armor => 0 }, warhammer => { cost => 25, damage => 6, armor => 0 }, longsword => { cost => 40, damage => 7, armor => 0 }, greataxe => { cost => 74, damage => 8, armor => 0 }, }; my $sum_cost; my $sum_damage; my $sum_armor; map { $sum_damage += $weapons_ref->{$_}->{damage}; $sum_cost += $weapons_ref->{$_}->{cost}; $sum_armor += $weapons_ref->{$_}->{armor} } keys %{ $weapons_ref }; print join(' ',$sum_cost, $sum_damage, $sum_armor), '\n';
157 30 0

This code is a little bit specific, while I think we should move towards more general code, to make reusing it more simpler. Let's refactor it:

use strict; use Data::Dumper; my $weapons_ref = { dagger => { cost => 8, damage => 4, armor => 0 }, shortsword => { cost => 10, damage => 5, armor => 0 }, warhammer => { cost => 25, damage => 6, armor => 0 }, longsword => { cost => 40, damage => 7, armor => 0 }, greataxe => { cost => 74, damage => 8, armor => 0 }, }; my $sum_result; my @sum_fields = qw/cost damage armor/; map { my $name = $_; map { $sum_result->{$_} += $weapons_ref->{$name}->{$_} } @sum_fields } keys %{ $weapons_ref }; print Dumper($sum_result);
$VAR1 = { 'armor' => 0, 'cost' => 157, 'damage' => 30 };

Next step is to refactor this code to a function, which will receive a hash, an array of fields that we like to sum, and return a hash with sums.

Replies are listed 'Best First'.
Re^2: using ref to hash of hash effectively
by eyepopslikeamosquito (Archbishop) on Dec 27, 2020 at 08:58 UTC

    Though it's nice to show different ways to do it, I feel obliged to point out that many folks disapprove of using map in void context.

    Though map in void context no longer suffers the huge performance penalty it once did, some still consider it poor style (see, for example, the performance-related discussion in this old thread).

    I personally applaud the simple stylistic advice summary given in Effective Perl Programming in the item "Use foreach, map and grep as appropriate", namely:

    • Use foreach to iterate read-only over each element of a list
    • Use map to create a list based on the contents of another list
    • Use foreach to modify elements of a list
    • Use grep to select elements in a list
    because I find the code clearer and easier to maintain if everyone in my team follows those four simple rules.

    Note that Perl::Critic provides a ProhibitVoidMap policy to allow the code police to prohibit using map in void context.

      Well, yes, there are some folks, that disapprove using our beloved language in some way that they think is wrong. But this is an aesthetical dispute, not technical. Some folks like it one way, other folks like it another way - and they do so they like, and all they do is beautiful (at least to themselves), and our main principle - multiple ways of doing one thing - flourishes.

      And in a spiritual matter, it is a god's blessing that recipes from no-map-in-void-context part of folks and recipes from the other folks has been given. In a dark times of which I think no need to mention, every single group, even one that think of using-map-in-void-context as of heresy, comes to help seeking wisdom in our monastery.

      And thank you for pointing out that our beloved monastery is not a dogmatic place. Yes, there are different monks that chant for gloriness with different means and using different techniques, and all as one are welcoming and kind to strangers - and to themselves. Such fellowship is a true blessing.

        I hope that we all can agree that when it comes to style issues, we should each be consistent. When one of these issues arises, we should form a standard which we are comfortable with and always use it.

        In this case, I like the standard suggested by eyepopslikeamosquito. For my own use, I have added another rule. "No map or grep block should produce any side effects."

        Bill

        In terms of aesthetics, my feeling is "each to his own". I used to use map in void context. I have no problem reading code like that. I no longer use it, but for completely different reasons (see below).

        The performance hit was resolved in 5.8.1 (see "perl581delta: Miscellaneous Enhancements"). If you're coding to 5.8.0 or earlier, you should avoid map in void context; otherwise, the performance issue is moot.

        Quite a few years ago, I benchmarked map against for. I was surprised at how much faster for was. That's the reason I would now choose

        for (keys %{ $weapons_ref }) { ... }

        over

        map { ... } keys %{ $weapons_ref };

        See Benchmark and run your own tests if you want.

        — Ken

Re^2: using ref to hash of hash effectively
by AnomalousMonk (Archbishop) on Dec 27, 2020 at 10:26 UTC

    Since the weapon names don't seem to matter, only their attributes, I think it's simpler to iterate over the values of the weapons hash per Cristoforo's approach. This works with map, but I think it's even simpler (and IMHO clearer) to use for-loops:

    Win8 Strawberry 5.8.9.5 (32) Sun 12/27/2020 5:07:00 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings -l use Data::Dumper; my $weapons_ref = { dagger => { cost => 8, damage => 4, armor => 0, }, shortsword => { cost => 10, damage => 5, armor => 0, }, warhammer => { cost => 25, damage => 6, armor => 0, }, longsword => { cost => 40, damage => 7, armor => 0, }, greataxe => { cost => 74, damage => 8, armor => 0, }, }; my $sum_result; my @sum_fields = qw/cost damage armor/; # map { # works # my $attrib = $_; # map { $sum_result->{$_} += $attrib->{$_} } @sum_fields # } values %$weapons_ref; for my $hr_attrib (values %$weapons_ref) { $sum_result->{$_} += $hr_attrib->{$_} for @sum_fields; } print Dumper $sum_result; ^Z $VAR1 = { 'cost' => 157, 'damage' => 30, 'armor' => 0 };


    Give a man a fish:  <%-{-{-{-<

      This is definitely the direction I was looking for! Both of these!

      The problem is part of adventofcode dot com - 2015 day 21, so not a homework problem per say, and I've already solved it in another language. This is really me wanting to learn how to use arrays and hashes of stuff more effectively.

      I really should have added that this is a Data Structure/Algorithm problem. In general, I want to learn how to structure the data so I can slice and dice it better. Leaving the data in two nested anonymous hashes isn't necessarily the goal. Rearranging the data to easily manipulate it would definitely be best!

      I have been trying to figure out how I could use List::AllUtils to get groups of data out of this grouping or set of data. Though, changing the initial structure may be a better solution. I don't know what I don't know. That is kind of the open question. When you get a set of data like this, and please look at this fun and interesting problem, is there some sort of best practice for what you check for to decide what type of data structures to put the data into? That would truly be the magical part!

        In general, I want to learn how to structure the data so I can slice and dice it better.
        Leaving the data in two nested anonymous hashes isn't necessarily the goal.
        Rearranging the data to easily manipulate it would definitely be best!
        Some famous programmers who agree with you

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11125780]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2024-04-19 05:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found