Search code examples
perlperl-data-structures

Defining a hash value using another hash value.


Is there a way to do the following while using only one data structure?

my %hash = (
  "key1" => "value1",
  "key2" => "value2",
  "key3" => $hash{key1},
);

So basically, I want to set a hash's key value to another key value. I've tried the above syntax but get the following warning:

Global symbol "%hash" requires explicit package name at ...

I'm assuming this is because I'm trying to reference the hash before it's actually been created.


Solution

  • Why not make your own function:

    use strict;
    use warnings;
    
    sub make_hash (@) { 
        my %h;
        my @unresolved;
        while ( @_ ) {
            my ( $key, $value ) = splice( @_, 0, 2 );
            next unless defined $value;
            if (   not ref( $value ) 
               and my ( $ref ) = $value =~ /^ref:\s*(.*\S)\s*$/ 
               ) {
                if ( defined( my $v = $h{ $ref } )) {
                    $h{ $key } = $v;
                }
                else {
                    push @unresolved, [ $key, $ref ];
                }
            }
            else {
                $value =~ s/^lit://;
                $h{ $key } = $value;
            }
        }
        $h{ $_->[0] } = $h{ $_->[1] } foreach grep { exists $h{ $_->[0] }; } @unresolved;
        return wantarray ? %h : \%h;
    } 
    

    To demonstrate some of the power:

    my %hash 
        = make_hash(
          'key1' => 'value1'
        , 'key2' => 'value2'
        , 'key3' => 'ref:key1'
        , 'key4' => 'lit:ref:key2'
        , 'key5' => 'lit:lit:ref:key3'
        );
    

    The lit: prefix covers the case of "What if I really wanted to pass a value that is a non-reference as 'ref:so-and-so'? It also is recursive in answering, "What if I direly need to make a value 'lit:xzy'.

    I've done this and I've also blessed a reference to a passed piece of data to a Lit class or something along those lines.

    sub Ref ($) { bless \shift, 'Ref' }
    

    And then in the make_hash routine you'd just check for ref( $value ) eq 'Ref'. And specify it like the following:

    my %hash 
        = make_hash(
          'key1' => 'value1'
        , 'key2' => 'value2'
        , 'key3' => Ref 'key1'
        );
    

    There are many ways to make Perl act like you wish it did.