[Templates] sorting a multi-valued hash

Josh Rosenbaum josh@infogears.com
Thu, 24 Aug 2006 10:08:07 -0600


Jonathan Mangin wrote:
> Is it possible to sort a multi-valued hash?
> In psuedo-code (all cols are text):
> 
>    $menu{id} = [concat(col2,col3), col2, col3];
> 
> I hoped it would default to sorting on value.0, but output
> appears random.
> 
>    [% FOREACH item IN menu.sort %]
>       [% IF menu.$item.1 == rows.0.1 %]
>          <option value="[% item %]" selected>[% menu.$item.0 %]
>       [% ELSE %]
>          <option value="[% item %]">[% menu.$item.0 %]
>       [% END %]
>    [% END %]
> 
> Two (or more) of these constructs in the template sort in
> the same order.  It must not be random but I can't see the
> pattern.  Is there anything else I can do to get a sort on
> value.0?


I think you'll need to create your own hash virtual method, or use a different structure.

Something like this might work:
menu = [
         {id => 'xxx', column1 => concat(col2,col3), columns => [col2, col3]},
         {id => 'xxx', column1 => concat(col2,col3), columns => [col2, col3]},
         {id => 'xxx', column1 => concat(col2,col3), columns => [col2, col3]},
       ]

Then [% FOREACH item IN menu.sort('column1') %]

Here's an example vmethod that works on your hash above. (Well maybe. I didn't test or think about it much):
$Template::Stash::HASH_OPS->{ sort_array } = sub {
  my ($hash, $index) = @_;
  return sort {$hash->{$a}->[$index] cmp $hash->{$b}->[$index] } keys %$hash;
};

Usage: [% FOREACH item IN menu.sort_array(0) %]

Of course you might want to make it a bit more robust than that, or change it to suit your needs.

-- Josh