Search code examples
arraysperlhashtemplate-toolkit

Array of sorted key/value pairs to sorted array of keys and hash


I am developing a Catalyst app which uses Template::Toolkit as template engine. One page needs a list of equal input elements. They can be taken from an array but I need both sort order and a descriptive label for the element.

For having a sort order I would use an array. For storing an additional value per key a hash is perfect. How to combine both in TT? I could use both things but that seems ugly and can cause mistakes when changing the fields.

However, I prefer doing this in TT because both the descriptions and the order of form elements is a front-end thing.

This is how I would do it in pure Perl:

#!/usr/bin/perl -w

use 5.10.0;

# definition of description and order in 1 step
my @fields = (
        property_foo => "Some property",
        property_bar => "Important field",
        property_baz => "Something else",
);

# extract information
my %descriptions = @fields;
my @order = @fields[grep {($_ + 1) % 2} 0..(scalar @fields - 1)];

say "=== natural perl sort order ===";
foreach (keys %descriptions) {say $_};

say "=== wanted output ===";
foreach (@order) {
        say $descriptions{$_} . ": [label for $_]";
}

Outputs:

=== natural perl sort order ===
property_baz
property_foo
property_bar
=== wanted output ===
Some property: [label for property_foo]
Important field: [label for property_bar]
Something else: [label for property_baz]

This is what I write in my template:

[%
order = (
    property_foo,
    property_bar,
    property_baz,
);

descriptions = {
    property_foo => "Some property",
    property_bar => "Important field",
    property_baz => "Something else",
}

FOREACH property IN order %]
    [% descriptions.$property %]: <input name="[% property %]" />
[% END %]

However, it is really ugly to have the same information (list of fields) twice. I want to avoid editing the list twice and with a longer list of fields it gets really annoying (about 20 items, not long enough to do some database stuff).


Solution

  • If you need ordering and multiple pieces of information then you should consider an array of hash references.

    my @fields = (
      { id => 'property_foo',
        label => 'Some property' },
      { id => 'property_bar',
        label => 'Important field' },
      { id => 'property_baz',
        label => 'Something else' },
    );
    
    foreach (@fields) {
      print "ID: $_->{id}, Label: $_->{label}\n";
    }
    

    If the complexity increases much beyond this, you might consider replacing the hashrefs with real objects.

    And, in TT, it looks like this:

    [%-
    properties = [
      {id => 'property_foo',
       label => 'Some property'},
      {id => 'property_bar',
       label => 'Important field'},
      {id => 'property_baz',
       label => 'Something else'},
    ];
    -%]
    
    [%- FOREACH property IN properties %]
        [% property.label %]: <input name="[% property.id %]" />
    [% END %]