Search code examples
perl

Convert hashref to array of kv pairs


Here's my initial code:

sub my_sub {
    my $hash = {
        age => 5, # default value for "age" key
        @_        # the rest of the "hash" as an array
    };
    ...
}

#used like so:
my_sub("age" => 42, ...);

But I'd like to also support taking in a hashref in addition to an array. So I've tried:

sub my_sub {
    my $hash = {
        age     => 5, # default value for "age" key
        ref(@_) eq "" ? @_ : %{$_[0]}
    };
    ...
}

If you call it with an array like before, the ref(@_) eq "" check would be true and the conditional would evaluate to @_ like before. But I can't for the life of me get the false case to work. Currently, it says there are an odd number of elements in the anonymous hash. But when I print out the value of %{$_[0]} I see the expected flattened array of (age, 42, ...) - in other words it looks right in the context of printing it out, but it barfs when it's in the conditional, and I have no idea why. Halp.


Solution

  • I'm not sure why you'd want to use a hash ref in the caller since it just makes the call noisier. But you could use

    sub my_sub {
       my %args = @_ == 1 ? %{ $_[0] } : @_;
    
       $args{ age } //= 5;
    
       ...
    }
    

    or

    sub my_sub {
       my $args = @_ == 1 ? $_[0] : { @_ };
    
       $args->{ age } //= 5;   # Warning: modifies caller
    
       ...
    }
    

    On the surface, the second is faster when provided a hash ref (since it doesn't build a new hash). But, in practice, a new hash is created in the caller each time, cancelling all benefits. On the plus side, that also means there's usually no consequences to modifying the hash (the issue identified by comment in the snippet).