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

Discussion in 'Perl Misc' started by Markus Dehmann, Mar 22, 2005.

  1. 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!
     
    Markus Dehmann, Mar 22, 2005
    #1
    1. Advertising

  2. Markus Dehmann <> writes:
    > 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
    --
    Come to think of it, there are already a million monkeys on a million
    typewriters, and Usenet is NOTHING like Shakespeare.
    -- Blair Houghton.
     
    Eric Schwartz, Mar 22, 2005
    #2
    1. Advertising

  3. Eric Schwartz wrote:

    > 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--

    --
    Cocoa programming in Perl: http://camelbones.sourceforge.net
    Hire me! My resume: http://www.dot-app.org
     
    Sherm Pendley, Mar 22, 2005
    #3
  4. Markus Dehmann wrote:
    > 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.

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Mar 22, 2005
    #4
  5. Markus Dehmann <> wrote:

    > 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.


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Mar 22, 2005
    #5
  6. Sherm Pendley wrote:
    > Eric Schwartz wrote:
    >> 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.


    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.

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
     
    Gunnar Hjalmarsson, Mar 22, 2005
    #6
  7. Markus Dehmann

    Eric Amick Guest

    On Tue, 22 Mar 2005 17:31:39 -0500, Markus Dehmann
    <> wrote:

    >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.

    --
    Eric Amick
    Columbia, MD
     
    Eric Amick, Mar 23, 2005
    #7
  8. Eric Amick wrote:
    > On Tue, 22 Mar 2005 17:31:39 -0500, Markus Dehmann
    > <> wrote:
    >
    >
    >>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.
     
    Brian McCauley, Mar 23, 2005
    #8
  9. Tad McClellan wrote:

    > Markus Dehmann <> wrote:
    >
    >> 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?


    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
     
    Markus Dehmann, Mar 23, 2005
    #9
  10. Markus Dehmann <> wrote:
    > Tad McClellan wrote:
    >> Markus Dehmann <> wrote:
    >>
    >>> I am using a two-dim hash with a string as first key, and an array as 2nd
    >>> key.



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



    > 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;


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Mar 23, 2005
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Denis Palas
    Replies:
    1
    Views:
    503
  2. decker
    Replies:
    9
    Views:
    335
    Barry Schwarz
    Nov 10, 2007
  3. Florian Kaufmann
    Replies:
    1
    Views:
    380
  4. Replies:
    3
    Views:
    182
  5. Sorting a two dim array

    , Jul 8, 2005, in forum: Javascript
    Replies:
    3
    Views:
    101
Loading...

Share This Page