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

Hello guys

Let there be a hash

%def = ( default_option_use_log => 1, default_option_read_conf => 1, default_option_run_debug => "false", default_output_path => "/tmp/vvv", default_name => "stat_", default_option_save_state => "true" );

We need to fill a new hash with keys based on filtered and changed keys of %def hash. Consider the following code:

foreach (map { /^default_option_(.*)/ ? $1 : () } keys %def) { $s{"option_$_"} = $def{"default_option_$_"}; } foreach (keys %s) { print "\%s: $_ => $s{$_}\n"; }

Is there a way to shorten the 'foreach' block or may be use different approach instead of 'foreach'. It would be great to eliminate double using 'default_option_' string, also '? : ()' condition looks not perfect. I need a nice-looking, laconic, as much Perl-ish as possible, solution.

Thanks

Replies are listed 'Best First'.
Re: get solution as short as possible
by moritz (Cardinal) on Aug 17, 2011 at 08:51 UTC

    A map with a ternary that returns () in one case is usually a good place for a grep:

    for (grep /^default_option_/, keys %def) { my (undef, $option) = split /_/, $_, 2; $s{$option} = $def{$_}; }

    (untested).

    Update: Another approach without an explicit loop, but with map/grep:

    my @keys = grep /^default_option_/, keys %def; my @short_keys = map +(split /_/, $_, 2)[1], @keys; @s{@short_keys} = @def{@keys};

    Regarding your wish to shorten the code, I can recommend The path to mastery.

      I really, really like your 3-line solution; especially the final line. I'm using that idiom in some coding right now. Thanks!
      for (grep /^default_option_/, keys %def) { my (undef, $option) = split /_/, $_, 2; $s{$option} = $def{$_}; }

      ... changes to a somewhat dense form of ...

      for ( grep /^default_option_/, keys %def ) { $s{ (split /_/, $_, 2)[-1] } = $def{ $_ }; }

      ... put other way (by way of moritz's explicit hint) ...

      %s = map { (split /_/, $_, 2)[-1] => $def{ $_ } } # Useless use of grep. grep /^default_option_/, keys %def ;

      ... which begs to go to the original version despite OP's request...

      %s = map { m/^ default_ (option_.+) $/x ? ( $1 => $def{ $_ } ) : () } keys %def ;

      I, myself, would not have bothered with any of map-grep or for-grep versions shown thus far. In case of for-loop, I would be prone to write "next if".

        second version avoids double using "default_option_" string but, in my opinion, is less straightforward than my original.

        Compare
        for ( grep /^default_option_/, keys %def ) { $s{ (split /_/, $_, 2)[-1] } = $def{ $_ }; }
        with original
        foreach (map { /^default_option_(.*)/ ? $1 : () } keys %def) { $s{"option_$_"} = $def{"default_option_$_"}; }
        Second case more distinctly highlights idea to use the subpart of key string as a new key. I wished to have something like
        for (grep /^default_option_(.*)/, keys %def) { $s{"option_$1"} = $def{$_}; }
        or even more short
        $s{"option_$1"} = $def{$_} for (grep /^default_option_(.*)/, keys %def +);

        Of course the last 2 solutions are incorrect. But if some monk knows how to achieve such level of laconism and clearness in one that would be a great pattern.

        Anyway I like your solutions for custom, rare cases. But I need a nice looking, straightforward and short version for everyday use. Of course if it exists in Perl 5 universe.

        Another problem is that your last solution truncates hash if it has data. It's not appropriate for me. Though I'm not sure if moritz's version does this.

        FYI: I've just registered. And this is my post

Re: get solution as short as possible
by ww (Archbishop) on Aug 17, 2011 at 10:51 UTC
    I second moritz' excellent suggestion for reading an excellent story.

    I can't endorse your desire "to shorten the 'foreach' block" nor support your "need (for) a nice-looking, laconic ... solution. Perhaps "nice-looking," shoudl have come out, since we may have very different ideas of the meaning of that phrase. Note that I did remove "as much Perl-ish as possible" from the quote.

    I did so because the "Perl-ish" solution should heed the advice to keep the code readily comprehensible; the warning that the individual writing a script do so as if the (future) maintainer would be a homicidal maniac who knows the original author's home address.

    Something of the same meme is enbedded in this portion of The path to mastery:

    "When you see the code of master Perl programmers you may be amazed at how few strokes of the keyboard they require to solve a problem completely. Many in error think that they should therefore constantly try to cram as much into as little room as possible.

    "This is a misguided path." (emphasis supplied)

    So read on, especially (jcwren) RE: The path to mastery by jcwren and, tangentially, dragonchild's Re^3: The path to mastery.

    Update: added sentence re "nice looking" in para 2.