http://www.perlmonks.org?node_id=1057626

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

I have the following data structure that I'm effectively treating as an ordered hash. I realize that I could use Tie::IxHash, but for various reasons (see below) I don't want to do that.

my $spec = { 'Parameters' => [ 'shipmentIncrementId' => { 'required' => 1, 'type' => 'string', }, 'carrier' => { 'required' => 1, 'type' => 'ups|usps|dhl|fedex|dhlint|custom', }, 'title' => { 'required' => 1, 'type' => 'string', }, 'trackNumber' => { 'required' => 1, 'type' => 'string', }, ], 'Response' => 'scalar', };

I'm iterating over the keys like so:

my @params = $thing->{'Parameters'}; for my $index (0..$#params) { next if $index % 2 != 0; my $parameter = $params[$index]; my $spec = $params[$index + 1]; # do something from here... }

Is there a more elegant way of doing that?

As to why I'm not doing Tie::IxHash: I'm using this to specify dynamic methods like so:

define_method 'foo' => { 'Parameters' => [ 'bar' => { 'required => 1, 'type' => 'string', }, 'baz' => { 'required => 1, 'type' => 'string', }, ] };

Note that the application requires the arguments to be in order. I'm not sure how I'd use Tie::IxHash in that situation.

Replies are listed 'Best First'.
Re: More elegant way of doing "ordered hash"?
by kcott (Archbishop) on Oct 10, 2013 at 08:00 UTC

    G'day PopeFelix,

    for my $index (0..$#params) { next if $index % 2 == 0; my $parameter = $params[$index]; my $spec = $params[$index + 1]; # do something from here... }

    Is there a more elegant way of doing that?

    How about:

    for (grep { not $_ % 2 } 0 .. $#params) { my ($parameter, $spec) = @params[$_, $_+1]; ... }

    [ You've got a couple of typos: the 1st $spec becomes $thing; you assign an arrayref to @params ]

    -- Ken

Re: More elegant way of doing "ordered hash"?
by boftx (Deacon) on Oct 09, 2013 at 22:19 UTC

    Right off the top it looks like the 'define_method' code is accepting the value for 'Parameters' as a hash ref instead of an array ref so I'm not sure you are going to be keeping the order of the keys to begin with, especially with the latest version of Perl.

    That aside, unless you are in a position to rewrite the app (which I would guess is significant in size) to use named parameters instead of positional you have probably come up with the best way to do what you want. The entire problem disappears if you can switch to named params. Plus, you can take advantage of CPAN modules to enforce type checking, etc, like you are currently doing.

    Of course, if you can make the change, then go whole-hog and covert to Moose since what you have here looks a lot like someone tried to invent Moose with positional params.

    On time, cheap, compliant with final specs. Pick two.

      Value for Parameters is an arrayref.

      As for sending named parameters, I can't do that because what I'm doing is wrapping XML-RPC calls to Magento, and I haven't found a way to get XML::RPC to send named params.

      And yes, this was partially inspired by Moose. :)

        whoops! There was a typo there. Parameters should definitely be an arrayref in define_api_method