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

Macrobiological Monks,

I have a list of user type codes, like this:

037 => 'member' 165 => 'public' 022 => 'staff' 683 => 'babe' 001 => 'old fart'
that are to be dynamically integrated into a drop-down html SELECT menu in an html form - but in a predetermined order, for example, in the order listed above. Neither alphabetical by label nor numerical by code. The user will select their option and submit the form for storage.

I imagine two options:

%user_types =( 037 => [ 'member', '1' ], 165 => [ 'public', '2' ], 022 => [ 'staff', '3' ], 683 => [ 'babe', '4' ], 001 => [ 'old fart', '5' ] );
Easy to look up the label of the stored code, but to sort for the original drop-down output, I ahve to sort by the value of $user_types{$code}[1]
OR
%user_types =( 1 => [ 'member', '037' ], 2 => [ 'public', '165' ], 3 => [ 'staff', '022' ], 4 => [ 'babe', '683' ], 5 => [ 'old fart', '001' ] );
and simply sort by the keys for output - simpler sort, BUT, when later I want to find the label of the option that was selected in the submitted form, it's more complicated.

Any better ideas?

Thanks




Forget that fear of gravity,
Get a little savagery in your life.

Replies are listed 'Best First'.
Re: Best method to order a hash of arrays
by jdporter (Paladin) on May 15, 2007 at 18:42 UTC

    I'd be inclined to do this, which pre-codes them in the desired order:

    @user_types =( [ '037' => 'member' ], [ '165' => 'public' ], [ '022' => 'staff' ], [ '683' => 'babe' ], [ '001' => 'old fart' ], );

    Then you can make a hash for looking up the labels by doing

    %lookup = map { @$_ } @user_types;

    However, if this is something that happens a lot in your code, you might consider making your hash order-remembering, e.g. by tieing it to Tie::IxHash:

    use Tie::IxHash; tie my %user_types, 'Tie::IxHash'; %user_types = ( '037' => 'member', '165' => 'public', '022' => 'staff', '683' => 'babe', '001' => 'old fart', );

    Then accessing the hash via functions like keys, values, and each gives you the data in the desired order.

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
Re: Best method to order a hash of arrays
by kyle (Abbot) on May 15, 2007 at 18:34 UTC

    I'd use an array of hashes.

    use List::Util qw( first ); my @user_types = ( { name => 'member', numb => '037', }, { name => 'public', numb => '165', }, ); my $selected = first { $_->{name} eq $cgi->param( 'foo' ) } @user_type +s; $selected ||= $user_types[0]; # in case nothing is selected my $selected_numb = $selected->{numb};

    If you don't want to use List::Util::first, you can use grep instead (the list is short). It's easy this way to add any info you want to each element (e.g., a 'key' you put in the HTML form plus a human friendly name that you show to the user). Inside your script you can tie as much data as you want to each element.

Re: Best method to order a hash of arrays
by shmem (Chancellor) on May 15, 2007 at 18:41 UTC
    To start with. I do not know where you get this list from, nor in what form it is stored in the first place:
    037 => 'member' 165 => 'public' 022 => 'staff' 683 => 'babe' 001 => 'old fart'

    Is it a hash or an array of hashes? an array of arrays?

    In either case, you have the original order handy, right? I'd use that for the <select>, passing in the user name as label and the index into the original list of tuples as value. If you retrieve the param(), that value serves you as index into the original list.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Best method to order a hash of arrays
by Herkum (Parson) on May 15, 2007 at 19:16 UTC

    When working with items that need to be a specific order, always use an array, avoid using hashes. While you can do a sort of a hash keys (for example) it is easy to forget that you had to have an order for that hash. Also it is easy to overwrite a hash key with a new entry and you may not notice. So use an array.

Re: Best method to order a hash of arrays
by xicheng (Sexton) on May 16, 2007 at 00:38 UTC
    You probably can try just one-dimentional array and then split each element on the fly. not sure about the trade-off, but it's much easier for me to maintain. :-)
    my @user_types =(
         '037 => member',
         '165 => public',
         '022 => staff',
         '683 => babe',
         '001 => old fart',
    );
    foreach my $option (@user_types) 
    {
       my ($user, $type) = split /\s*=>\s*/, $option;
       make_option($user, $type);
    }
    
    Regards,
    Xicheng
Re: Best method to order a hash of arrays
by punch_card_don (Curate) on May 15, 2007 at 19:38 UTC
    Well, all very interesting, thanks, but I think I'm going to have to go with
    <code> # code => [ 'label', 'order' ] %user_types =( 037 => [ 'member', '1' ], 165 => [ 'public', '2' ], 022 => [ 'staff', '3' ], 683 => [ 'babe', '4' ], 001 => [ 'old fart', '5' ] );
    and
    @types_sorted = sort { $user_types{$a}[1] <=> $user_types{$b}[1] } ke +ys %user_types; for $i (0 .. $#types_sorted) { make options list; }
    It's simple & readable




    Forget that fear of gravity,
    Get a little savagery in your life.

      You should be aware that your code, above, creates a hash with keys "37", "165", "22", "683", "1".
      Unfortunately, if you want the leading zeroes, you'll need to explicitly quote the keys.

      A word spoken in Mind will reach its own level, in the objective world, by its own weight

        Actually, it's worse than that. The leading zero causes Perl to interpret it as an octal value, so 037 becomes '31'.

        use Data::Dumper; my %user_types =( 037 => [ 'member', '1' ], 165 => [ 'public', '2' ], 022 => [ 'staff', '3' ], 683 => [ 'babe', '4' ], 001 => [ 'old fart', '5' ] ); print Dumper( \%user_types ); __END__ $VAR1 = { '1' => [ 'old fart', '5' ], '18' => [ 'staff', '3' ], '165' => [ 'public', '2' ], '683' => [ 'babe', '4' ], '31' => [ 'member', '1' ] };