Reputation: 577
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
Reputation: 1
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
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
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
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
Reputation: 57640
No. Following workarounds are possible:
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.
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.
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
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