key_
key_

Reputation: 577

alias a hash element in perl

Is it possible to access the same value under different hash keys? How can I tell Perl not to copy the "very long text?"

$hash->{'key'} = 'very long text';
$hash->{'alias'} = $hash->{'key'};

Upvotes: 4

Views: 1481

Answers (6)

dfen
dfen

Reputation: 1

Use Hash::Util::hv_store

The standard Hash::Util module includes the hv_store() subroutine which can be used to alias hash values together. It uses references, but you can access elements with normal syntax, without needing an extra scalar dereference like the previous answers.

This example uses hv_store() to alias some keys together, then changes a couple values and uses Devel::Peek to demonstrate that the aliases still point to the same values as each other.

use warnings;
use strict;
use Hash::Util;
use Devel::Peek; # only for demonstration; not required to use aliases

# this utility sub turns each pipe-separated list of keys into aliases to
# the same scalar value, within the hashref provided in the first argument
sub alias_keys {
    my $href = shift;
    while (@_) {
        my ($aliases, $value) = splice @_, 0, 2;
        for my $key (split /[|]/, $aliases) {
            Hash::Util::hv_store(%$href, $key, $value) or die;
        }
    }
}

# here a hash is initialized, then some aliases are assigned, and then a
# values are changed, to show that other aliases refer to the new value
my %hash = ('default' => 2, 'n' => 1);
alias_keys(\%hash,
    'dryrun|test|n' => 0,
    'pidfile|p' => '/var/run/sample'
);
$hash{'option'} = 1;
$hash{'p'} = '/var/run/changed';

Devel::Peek::Dump(%hash, 10);

(The output here is unavoidably dense.) Note that the aliased keys dryrun, test and n all refer to the same scalar value at 0x800e5f4c8, and the other aliased pair pidfile and p refer to a shared value at 0x800e5cb88. The code uses %hash with normal syntax, but changing the value of one aliased key is visible when accessed by another name. In these examples, the $hash{n} and $hash{pidfile} values are seen with new values propagated from their aliases.

Contrast those with the non-aliased keys default and option, which have unique SV locations, and a telltale reference count of 1. This shows that even with some aliased keys, the hash continues to work normally, without the extra syntax that would be required to use references explicitly.

SV = PVHV(0x800e21ce0) at 0x800e5f378
  REFCNT = 1
  FLAGS = (SHAREKEYS)
  ARRAY = 0x800e4da80  (0:9, 1:7)
  hash quality = 175.0%
  KEYS = 7
  FILL = 7
  MAX = 15
  Elt "default" HASH = 0x71c7e975
  SV = IV(0x800e1f668) at 0x800e1f678
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 2
  Elt "pidfile" HASH = 0xf60020c7
  SV = PV(0x800e27e90) at 0x800e5cb88
    REFCNT = 2
    FLAGS = (POK,IsCOW,pPOK)
    PV = 0x800ff0440 "/var/run/changed"\0
    CUR = 16
    LEN = 18
    COW_REFCNT = 1
  Elt "test" HASH = 0xb6316ff9
  SV = IV(0x800e5f4b8) at 0x800e5f4c8
    REFCNT = 3
    FLAGS = (IOK,pIOK)
    IV = 0
  Elt "p" HASH = 0x1009461a
  SV = PV(0x800e27e90) at 0x800e5cb88
    REFCNT = 2
    FLAGS = (POK,IsCOW,pPOK)
    PV = 0x800ff0440 "/var/run/changed"\0
    CUR = 16
    LEN = 18
    COW_REFCNT = 1
  Elt "n" HASH = 0x3751d09c
  SV = IV(0x800e5f4b8) at 0x800e5f4c8
    REFCNT = 3
    FLAGS = (IOK,pIOK)
    IV = 0
  Elt "dryrun" HASH = 0xe4c9ad9e
  SV = IV(0x800e5f4b8) at 0x800e5f4c8
    REFCNT = 3
    FLAGS = (IOK,pIOK)
    IV = 0
  Elt "option" HASH = 0x3e940e4f
  SV = IV(0x800e1f980) at 0x800e1f990
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 1

It looks like hv_store() has been in Perl core since 5.8.9. I found this technique useful with Getopt::Long, which allows option name aliases, but only stores the value in the first provided key. By aliasing the different variations to the same scalar value, I could refer to option names in the code in the same manner as on the command line.

Upvotes: 0

ikegami
ikegami

Reputation: 386461

The simple way is to use a reference to a common variable.

my $hash;
my $val = 'very long text';
$hash->{key} = \$val;
$hash->{alias} = $hash->{key};

say ${ $hash->{key} };        # very long text
say ${ $hash->{alias} };      # very long text

${ $hash->{key} } = 'some other very long text';

say ${ $hash->{key} };        # some other very long text
say ${ $hash->{alias} };      # some other very long text

say $hash->{key} == $hash->{alias} ? 1 : 0;  # 1

The complicated way is to use Data::Alias.

use Data::Alias qw( alias );

my $hash;
$hash->{key} = 'very long text';
alias $hash->{alias} = $hash->{key};

say $hash->{key};        # very long text
say $hash->{alias};      # very long text

$hash->{key} = 'some other very long text';

say $hash->{key};        # some other very long text
say $hash->{alias};      # some other very long text

say \$hash->{key} == \$hash->{alias} ? 1 : 0;  # 1

Upvotes: 6

tuxuday
tuxuday

Reputation: 3037

use array ref instead of scalar.

use Data::Dumper ;

my $Var = [10];
my %Hash = ('k' => $Var, 'a' => $Var);

print Dumper \%Hash;
$Hash{'a'}[0] = 'test' ;
print Dumper \%Hash;

Upvotes: 2

Oesor
Oesor

Reputation: 6642

Tie::AliasHash will work, though I wouldn't reccommend going this route. What are you trying to do that you feel you need to alias hash keys? There's likely a better route to go.

Upvotes: 5

amon
amon

Reputation: 57640

No. Following workarounds are possible:

Just copy it

Unless we are talking about megabytes over megabytes of data, or unless we are doing this a few thousand times, just copying the data around won't hurt too much.

Using references

my %hash = (key => 'very long text');
my $reference = \($hash{key});

print "The very long text is ", $$reference, "\n";

or

$hash{alias} = \($hash{key});

print "The very long text is ", $$hash{alias}, "\n";

Drawback: you have to use different syntax.

Using variables as keys

my $key = "key";
my $alias = $key;

my %hash = ($key => 'very long text');

print "The very long text is ", $hash{$alias}, "\n";

Drawback: extra vars.

Upvotes: 1

perreal
perreal

Reputation: 98088

You can use a reference:

$hash->{'key'} = 'very long text';
$hash->{'alias'} = \$hash->{'key'};
print ${$hash->{alias}}, "\n" if ref $hash->{alias};

Upvotes: 0

Related Questions