tamaguchi has asked for the wisdom of the Perl Monks concerning the following question:

Suppose I have a datastructure which is both a normal hash and a hash of arrays. Such as this one:
my %hash; my @arr=('first', 'second', 'third'); $hash{'A'}=100; $hash{'B'}=200; $hash{'C'}=[@arr];
Could you suggest a general loop to access the ellements of this structure, for example for outprinting?

Replies are listed 'Best First'.
Re: Complex hash?
by imp (Priest) on Aug 24, 2006 at 20:23 UTC
    That depends - what sort of output do you want?

    If this is for debugging then Data::Dumper or one of its variants are good.

    Otherwise you could try Data::Walk, which lets you walk over the nodes of a complex structure.

    Update - Actually I don't see a way to obtain the current key/index using Data::Walk, so this is probably not an appropriate solution for you.

Re: Complex hash?
by jeffa (Bishop) on Aug 24, 2006 at 20:28 UTC

    I don't recommend this, but just so you can see how you can do it:

    for (sort keys %hash) { print "$_ => "; if (ref($hash{$_}) eq 'ARRAY') { print join(', ', @{ $hash{$_} }); } elsif (ref($hash{$_}) eq 'HASH') { # do something else } else { print $hash{$_}; } print "\n"; }
    I'll leave handling the HASH case as your excercise. ;)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    

      Consider:

      use strict; use warnings; use Tk; my $mw = MainWindow->new (-title => 'Hello World'); my @arr=('first', 'second', 'third'); my %hash = (A => 100, B => 200, C => [@arr], D => \&hi, E => $mw); $hash{'A'}=100; $hash{'B'}=200; $hash{'C'}=[@arr]; for (sort keys %hash) { print "$_ => "; if (ref($hash{$_}) eq 'ARRAY') { print "Array: ", join(', ', @{ $hash{$_} }); } elsif (ref($hash{$_}) eq 'HASH') { # do something else print "Hash: ..."; } else { print "Scalar: $hash{$_}"; } print "\n"; } sub hi { print "hello World"; }

      Prints:

      A => Scalar: 100 B => Scalar: 200 C => Array: first, second, third D => Scalar: CODE(0x21bdf50) E => Scalar: MainWindow=HASH(0x21d97ac)

      Adding:

      elsif (ref($hash{$_})) { print "Unknown ref: $hash{$_}";

      alters the output to:

      A => Scalar: 100 B => Scalar: 200 C => Array: first, second, third D => Unknown ref: CODE(0x21be538) E => Unknown ref: MainWindow=HASH(0x21d9e38)

      References come in many flavours. There is probably no good general answer to OP's question for "display" output (as opposed to debugging output), but at least unhandled cases can be caught by noting that ref returns undef for non-reference values.


      DWIM is Perl's answer to Gödel
Re: Complex hash?
by planetscape (Chancellor) on Aug 25, 2006 at 00:59 UTC
Re: Complex hash?
by mreece (Friar) on Aug 24, 2006 at 20:34 UTC
    if you don't care what hash key order, you can do simply:

    my @values = map { (ref $_ eq 'ARRAY') ? @$_ : $_ } values %hash;

    if you do care, this works:

    my @values = map { (ref $hash{$_} eq 'ARRAY') ? @{$hash{$_}} : $hash{$_} } sort keys %hash;
      if (ref($hash{$_}) eq 'ARRAY')

      I dont understand this.. Does the word 'ARRAY' have a special meaning in this context? What more such special words are there, and where is it possible to read about this? Appearently it does not work to write 'array' in lc.
        The reason that 'array' doesn't match is that you are comparing the return value of ref with the string 'array'. ref returns what sort of reference something is, and in the case of arrays that string is 'ARRAY'

        See ref for more information.

        ref returns the following values:
        • SCALAR
        • ARRAY
        • HASH
        • CODE
        • REF
        • GLOB
        • LVALUE
        And it those that you must test for - depending on what you are looking for.