Two-dim array: Strange "can't use string as ARRAY ref"

M

Markus Dehmann

I am using a two-dim hash with a string as first key, and an array as 2nd
key. But it doesn't work! Can anyone tell me what's wrong?

#!/usr/bin/perl -w
$hash{"test"}{["one", "two"]} = 42;
$hash{"test"}{["three", "four"]} = 23;
my $key = "test";
foreach(keys %{$hash{$key}}){
print "Value for $_ is $hash{$key}{$_}\n"; # ARRAY(0x804c958)
# print "@$_\n"; # Can't use string ("ARRAY(0x804c958)") as an ARRAY ref
}

The output is:
Value for ARRAY(0x804c958) is 23

Why does it print only *one* value? Why can't I dereference
the array ref $_?

Thanks!
 
E

Eric Schwartz

Markus Dehmann said:
I am using a two-dim hash with a string as first key, and an array as 2nd
key. But it doesn't work! Can anyone tell me what's wrong?

You can't use an array as a hash key. The very first paragraph of
perldata says:

Hashes are unordered collections of scalar values indexed by
their associated string key.

So when you try to index a hash with an array like this:
$hash{"test"}{["one", "two"]} = 42;

what's happening instead is that ["one","two"] is being stringified
into something like ARRAY(0x804c958), and that string is what your
second-level hash is being subscripted by.
The output is:
Value for ARRAY(0x804c958) is 23

Why does it print only *one* value? Why can't I dereference
the array ref $_?

Because it's not an array ref anymore, it's a string. Only strings
can be stored as the keys of hashes-- the values can be anything you
like.

-=Eric
 
S

Sherm Pendley

Eric said:
You can't use an array as a hash key.

It's not impossible to use array refs as keys, but it does require some
additional steps that aren't needed when you use strings as keys. Have a
look at Tie::RefHash. (It's a core module in my copy of 5.8.1 - I don't
know about earlier.)

sherm--
 
G

Gunnar Hjalmarsson

Markus said:
I am using a two-dim hash with a string as first key, and an array as 2nd
key.

No you are not. A hash key cannot be an array.

But it doesn't work! Can anyone tell me what's wrong?
#!/usr/bin/perl -w
$hash{"test"}{["one", "two"]} = 42;
$hash{"test"}{["three", "four"]} = 23;

Try this:

use Data::Dumper;
print Dumper \%hash;
my $key = "test";
foreach(keys %{$hash{$key}}){
print "Value for $_ is $hash{$key}{$_}\n"; # ARRAY(0x804c958)
# print "@$_\n"; # Can't use string ("ARRAY(0x804c958)") as an ARRAY ref
}

The output is:
Value for ARRAY(0x804c958) is 23

Why does it print only *one* value?

Because there is only one value.
Why can't I dereference the array ref $_?

See Eric's answer about that.
 
T

Tad McClellan

Markus Dehmann said:
I am using a two-dim hash with a string as first key, and an array as 2nd
key.


You cannot have arrays as keys in Perl hashes.

But it doesn't work!


What were you hoping that it would do?

Can anyone tell me what's wrong?


You are trying to create an impossible data structure.

If you had told us what you really want, then we would have a
chance of showing you how to refactor your data structure.

#!/usr/bin/perl -w
$hash{"test"}{["one", "two"]} = 42;


Hash keys are forced to be strings.

If you give it an array ref as a hash key, you will get a stringified
representation of the reference, which can no longer be used to
access the array.

$hash{"test"}{["three", "four"]} = 23;


Do you want the 2nd level hash to have 4 key/value pairs in it?

$hash{test}{one} = 42;
$hash{test}{two} = 42;
$hash{test}{three} = 23;
$hash{test}{four} = 23;

or, by using a "hash slice":

@hash{ qw/ one two three four / } = (42, 42, 23, 23);


If you wanted something else, try telling us what that something else is.

my $key = "test";
foreach(keys %{$hash{$key}}){
print "Value for $_ is $hash{$key}{$_}\n"; # ARRAY(0x804c958)
# print "@$_\n"; # Can't use string ("ARRAY(0x804c958)") as an ARRAY ref
}

The output is:
Value for ARRAY(0x804c958) is 23

Why does it print only *one* value?


Because there is only one key ("test") in the hash, your 2nd
assignment stomped over the value of the 1st assignment.

Why can't I dereference
the array ref $_?


Because it is NOT an array ref, it is a string.
 
G

Gunnar Hjalmarsson

Sherm said:
It's not impossible to use array refs as keys, but it does require some
additional steps that aren't needed when you use strings as keys. Have a
look at Tie::RefHash.

While giving Eric right, "perldoc perlref" mentions that Tie::RefHash
provides a *workaround*. The distinction seems to be important to
understand this limitation with Perl hashes.
 
E

Eric Amick

I am using a two-dim hash with a string as first key, and an array as 2nd
key. But it doesn't work! Can anyone tell me what's wrong?
#!/usr/bin/perl -w
$hash{"test"}{["one", "two"]} = 42;
$hash{"test"}{["three", "four"]} = 23;

Hash keys are strings, so the array reference is converted to a string.
You need to look at the Tie::RefHash module, which I believe is a core
module, to do what you're after.
 
B

Brian McCauley

Eric said:
I am using a two-dim hash with a string as first key, and an array as 2nd
key. But it doesn't work! Can anyone tell me what's wrong?

#!/usr/bin/perl -w
$hash{"test"}{["one", "two"]} = 42;
$hash{"test"}{["three", "four"]} = 23;


Hash keys are strings, so the array reference is converted to a string.
You need to look at the Tie::RefHash module, which I believe is a core
module, to do what you're after.

But if using Tie::RefHash you can't look up an element using
$hash{"test"}{["three", "four"]} because the ["three", "four"] creates a
new array. It may have the same content as the array that's used as a
key but it is not the same array.
 
M

Markus Dehmann

Tad said:
You cannot have arrays as keys in Perl hashes.


What were you hoping that it would do?

Thanks for all your comments. Now I know why it didn't work.
If you had told us what you really want, then we would have a
chance of showing you how to refactor your data structure.

Do you want the 2nd level hash to have 4 key/value pairs in it?

$hash{test}{one} = 42;
$hash{test}{two} = 42;
$hash{test}{three} = 23;
$hash{test}{four} = 23;

or, by using a "hash slice":

@hash{ qw/ one two three four / } = (42, 42, 23, 23);

If you wanted something else, try telling us what that something else is.

If you know C++ and STL you'll understand this data structure:

map< string, map< set<string>, double > > myHash;

That's what I wanted in Perl.

I understand that I could use Tie::RefHash. But I now used another
workaround:

$hash{test}{"one two"} = 42;
$hash{test}{"three four"} = 23;

Instead of the array as a 2nd key, I use a string "one two" that I just
split when I work with it.

Markus
 
T

Tad McClellan

Markus Dehmann said:
But I now used another
workaround:

$hash{test}{"one two"} = 42;
$hash{test}{"three four"} = 23;

Instead of the array as a 2nd key, I use a string "one two" that I just
split when I work with it.


Maybe you want a _3_ dimensional data structure?

$hash{test}{one}{two} = 42;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top