in reply to Turning a hash into a line of links

Other people have mentioned join, which is the right tool for this job. You're also right in thinking of map as being equivalent to the explicit loop. They are actually very similar - in both cases you can actually modify the loop variable (or map variable) and change the underlying array.

Personally, I tend to use map< for "making a new X for every y in the array", i.e. when I want to create a different list of N items, as is the case here. If I want a side-effect (e.g. printing, or calling a function to process every y in the array) I tend to write the explicit loop for clarity. map has that 'pipeline' feel to it.

You mentioned having a leading space on one of the keys, in order to force the sort order.

This is ingenious, but leaves a space in your output and doesn't scale easily to giving your greater control over the sort order. If you want to do that, one way to extend your idea would be to add a prefix, say A_, B_, etc, to each key, in the order you want them displayed.

You can then include the prefix stripping as part of your processing, which brings us to map's close relative (evil twin?) apply. This is almost identical to map, but works on a copy of the items from the array, so it appropriate when you want to change the value of $_ in the processing step (as we do here, to strip off the sorting tag at the front):

#!perl -w use strict; use List::MoreUtils qw/apply/; my %sections=( A_DMA => 'dma', B_FST => 'dma/fst', C_MRA => 'dma/mra', D_BKS => 'bks', E_MSU => 'dma/msu', F_FMA => 'dma/fma', G_FOMC => 'dma/FOMC', ); print join("\n", apply { s/^\w_//; } sort keys %sections);
Note that you need to pull in apply from List::MoreUtils, where you'll also find lots of other goodies in a similar vein, allowing you to replace loops with simpler, hopefully clearer, code.

Replies are listed 'Best First'.
Re^2: Turning a hash into a line of links
by johngg (Canon) on Dec 13, 2006 at 10:19 UTC
    I think that mangling the keys by adding a prefix could quickly get out of hand and become difficult to manage, with strategies required if the number of sections goes over 26 etc. If you have one particular key, as in the OP, that has to come first then sort on whether you have found that key before the lexical sort, like this

    #!/usr/local/bin/perl -l # use strict; use warnings; my %sections = ( DMA => q{dma}, FST => q{dma/fst}, MRA => q{dma/mra}, BKS => q{bks}, MSU => q{dma/msu}, FMA => q{dma/fma}, FOMC => q{dma/FOMC}); my $thisKeyFirst = q{DMA}; my @sortedKeys = map { $_->[0] } sort { $b->[1] <=> $a->[1] || $a->[0] cmp $b->[0] } map { [$_, $_ eq $thisKeyFirst] } keys %sections; print for @sortedKeys;

    which produces

    DMA BKS FMA FOMC FST MRA MSU

    It seems to me that it would be easier to manage that way. It you had a few special keys that fell into this category this method would still work but with a hash to hold the order of the specials, highest value first and keys not in specials hash getting zero. Something like (not tested)

    my %specials = ( DMA => 2, MSU => 1); ... map { [$_, exists $specials{$_} ? $specials{$_} : 0] } keys %sections;

    Cheers,

    JohnGG

      I think at that point, it would be better to just maintain a real order, via a list, and associating values as part of an array ref.
      my @sections = ( [DMA => 'dma'], [FST => 'dma/fst'], [MRA => 'dma/mra'], [BKS => 'bks'], [MSU => 'dma/msu'], [FMA => 'dma/fma'], [FOMC => 'dma/FOMC'], ); my @la = map { a( {-href => "/$_->[1]"}, "$_->[0] HOME" ) } @sections; print join "\n|", @la;
      In fact that's probably better than the key-mangling for this number of keys too.
        The problem with that is you lose the ability to do look-ups so you have to iterate over the array to find the 'FMA' key/value pair. You could keep the hash but make it a HoH with the sort order held inside, like

        my %sections = ( DMA = {attrib => q{dma}, sortOrder => 1}; ...

        but that strikes me as more complicated when most keys don't need it, just those with an odd ordering requirement.

        Cheers,

        JohnGG