trying to understand hash of hashes or multidimensional arrays

Discussion in 'Perl Misc' started by Sherm Pendley, Jun 23, 2005.

  1. writes:

    > This problem may be something simple that I am failing to grasp ...
    > I've read dozens of postings about 'hashes of hashes' and
    > 'multidimensional hashes' and 'hashes of arrays', and I still can't
    > figure out how to use such a structure to do what I want.


    Reading "perldoc perllol", and "perldoc perldsc" would be a great start.

    sherm--
     
    Sherm Pendley, Jun 23, 2005
    #1
    1. Advertising

  2. Sherm Pendley

    Guest

    This problem may be something simple that I am failing to grasp ...
    I've read dozens of postings about 'hashes of hashes' and
    'multidimensional hashes' and 'hashes of arrays', and I still can't
    figure out how to use such a structure to do what I want.

    I have a list that I read into an array. Each element is a text string
    like this (it's an object identifier):

    "region.level.location.system"

    There's an unknown number of regions, then for each region there are an
    unknown number of levels, in each level an unknown number of locations,
    and in each location an unknown number of systems.

    The labels or names for each node of the object name may be duplicated
    within different 'branches' ... they are unique only within their
    context of 'region.level.location.system'. For example, there can be
    multiple instances of a 'location' called 'home' as long as each 'home'
    is in a different 'region' or 'level'.

    So I want to read this text data into my script, store it in a
    multidimensional array (or should it be a 'hash of hashes'?). Then I
    need to generate a report that shows me the relationships in an orderly
    (almost graphic) manner ... I want to be able to print out something
    like this (actual formatting is not important, just the organization):

    region1

    region1.level1
    region1.level2

    region1.level1.location1
    region1.level1.location2

    region1.level2.location1
    region1.level2.location2

    region1.level1.location1.systemA
    region1.level1.location1.systemB

    region1.level1.location2.systemA
    region1.level1.location2.systemB

    region1.level2.location1.systemA
    region1.level2.location1.systemB


    .... so on and so forth ... iterating through every region, every
    level, every location, every system

    So how can I put the data into a multidimensional array or hash and
    then pull that data back out as needed? I've thought of using multiple
    arrays and then having elements of arrays referring to other arrays,
    but that gives me a headache and I really believe this should be doable
    via a single multidimensional hash ... I just can't figure out how.

    .....

    More on the issue of multidimensional hashes ... I've seen these 2
    different structures, and I know they are functionally different, but I
    don't understand why and how each should be used:

    (example 1) $hashname{$key{$subkey}}

    (example 2) $hashname($key}{$subkey}

    Can someone explain how these structures are used?

    thanks & regards,

    John
     
    , Jun 23, 2005
    #2
    1. Advertising

  3. Sherm Pendley

    Guest

    .... forgive the typo above ... "example 2" should be:
    $hashname{$key}{$subkey}

    John
     
    , Jun 23, 2005
    #3
  4. wrote in news:1119483448.237142.244340
    @g44g2000cwa.googlegroups.com:

    > This problem may be something simple that I am failing to grasp ...
    > I've read dozens of postings about 'hashes of hashes' and


    Well, you should always start with Perl documentation rather than random
    postings.

    See

    perldoc perlreftut

    as well as

    perldoc perldsc

    > I just can't figure out how.


    Make an attempt, and we can help you with any hurdles you encounter.

    > (example 1) $hashname{$key{$subkey}}
    >
    > (example 2) $hashname($key}{$subkey}


    Example 2 is a syntax error.

    Sinan

    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
     
    A. Sinan Unur, Jun 23, 2005
    #4
  5. <> kirjoitti 22.06.2005:
    >
    > I have a list that I read into an array. Each element is a text string
    > like this (it's an object identifier):
    >
    > "region.level.location.system"


    Okay, you can store this in a 4-dimensional hash like this:

    my %hash;
    foreach my $string (@array) {
    my ($region, $level, $location, $system) = split /\./, $string;
    $hash{$region}{$level}{$location}{$system}++;
    }

    The values in the hash will simply be numbers indicating how many
    times each identifier has been seen. You don't need to use them for
    anything -- it's just the keys that matter.


    > need to generate a report that shows me the relationships in an orderly
    > (almost graphic) manner ... I want to be able to print out something
    > like this (actual formatting is not important, just the organization):
    >
    > region1
    >
    > region1.level1
    > region1.level2
    >
    > region1.level1.location1
    > region1.level1.location2

    [snip]
    >
    > ... so on and so forth ... iterating through every region, every
    > level, every location, every system


    For the output you want, the simplest way would be to write four
    separate loops, one for each depth. They will all look fairly
    similar:

    foreach my $region (sort keys %hash) {
    print "$region\n";
    }

    foreach my $region (sort keys %hash) {
    my %region = %{ $hash{$region} };
    foreach my $level (sort keys %region) {
    print "$region.$level\n";
    }
    }

    foreach my $region (sort keys %hash) {
    my %region = %{ $hash{$region} };
    foreach my $level (sort keys %region) {
    my %level = %{ $region{$level} };
    foreach my $location (sort keys %level) {
    print "$region.$level.$location\n";
    }
    }
    }

    foreach my $region (sort keys %hash) {
    my %region = %{ $hash{$region} };
    foreach my $level (sort keys %region) {
    my %level = %{ $region{$level} };
    foreach my $location (sort keys %level) {
    my %location = %{ $level{$location} };
    foreach my $system (sort keys %location) {
    print "$region.$level.$location.$system\n";
    }
    }
    }
    }

    Of course, if you wanted to do the same for a nested hash of arbitrary
    depth, you could use a recursive solution:

    sub keys_at_depth {
    my ($hashref, $depth) = @_;
    return sort keys %$hashref if $depth < 2;
    my @keys;
    foreach my $key (sort keys %$hashref) {
    my $subhash = $hashref->{$key};
    next unless ref $subhash eq 'HASH';
    push @keys, map "$key.$_", keys_at_depth($subhash, $depth-1);
    }
    return @keys;
    }

    my $depth = 1;
    while (my @keys = keys_at_depth(\%hash, $depth)) {
    print "$_\n" for @keys;
    $depth++;
    }

    There, that's much nicer, isn't it? Of course, there are several
    other ways to approach your problem as well. For example, you could
    instead use four separate hashes, one for each depth:

    my (%regions, %levels, %locations, %systems);
    foreach my $string (@array) {
    my ($region, $level, $location, $system) = split /\./, $string;
    $regions {"$region"}++;
    $levels {"$region.$level"}++;
    $locations{"$region.$level.$location"}++;
    $systems {"$region.$level.$location.$system"}++;
    }

    print "$_\n" for sort keys %regions;
    print "$_\n" for sort keys %levels;
    print "$_\n" for sort keys %locations;
    print "$_\n" for sort keys %systems;

    Or you could use an array of hashes:

    my @depths;
    foreach my $string (@array) {
    my @parts = split /\./, $string;
    $depths[$_]{join ".", @parts[0 .. $_]}++ for 0 .. $#parts;
    }

    foreach my $depth (@depths) {
    print "$_\n" for sort keys %$depth;
    }

    There, I don't think it gets any more compact than that. Of course,
    to populate @array, you'd use:

    my @array = <>;
    chomp @array;

    and don't forget to start your script with:

    #!/usr/bin/perl
    use warnings;
    use strict;

    --
    Ilmari Karonen
    To reply by e-mail, please replace ".invalid" with ".net" in address.
     
    Ilmari Karonen, Jun 23, 2005
    #5
  6. Sherm Pendley

    Guest

    My first posting of 'example 2' has a paren where there should be a
    curly, but to match the actual code I found in a script, it should be
    like this:

    $hashname{$key}{bareword} = 'value';

    I know this structure is legal (well, it works). I extrapolated that
    'bareword' could be replaced with a variable, hence it should work as

    $hashname{$key}{$subkey} = 'value';

    What is the functional difference between that and

    $hashname{$key{$subkey}} = 'value';

    ?
    .....

    I scanned the perldocs perllol and perldsc ... the latter one helped
    immensely ... I now have a working script using the HoH structure as
    in example 1. Thank you, Sherm & Sinan.
     
    , Jun 23, 2005
    #6
  7. wrote in news:1119560161.977333.248710
    @f14g2000cwb.googlegroups.com:

    > What is the functional difference between that and
    >
    > $hashname{$key{$subkey}} = 'value';


    Does this help?

    my %key = (
    # blah blah
    );
    my $x = $key{$subkey};

    my %hashname = (
    # blah blah
    );

    $hashname{$x} = 'value';

    Now, instead of $x, use $key{$subkey}:

    $hashname{$key{$subkey}} = 'value';

    which is different than

    $hashname{$key}{$subkey} = 'value';

    Indeed, the latter is the case where the names $key and $subkey convey
    the intended meaning.

    You should use Data::Dumper and check out the structure you end up with
    following various operations.

    And please quote an appropriate amount of context when you reply.

    Sinan
    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
     
    A. Sinan Unur, Jun 23, 2005
    #7
  8. Sherm Pendley

    Eric Bohlman Guest

    wrote in news:1119560161.977333.248710
    @f14g2000cwb.googlegroups.com:

    > I know this structure is legal (well, it works). I extrapolated that
    > 'bareword' could be replaced with a variable, hence it should work as
    >
    > $hashname{$key}{$subkey} = 'value';
    >
    > What is the functional difference between that and
    >
    > $hashname{$key{$subkey}} = 'value';
    >
    > ?


    In the first case, you have a hash (%hashname) and you're "subscripting" it
    with two scalar variables, $key and $subkey. In the second case, you have
    *two* hashes (%hashname and %key) and only one scalar ($subkey); you are
    *not* making any use of a scalar called $key. Instead you're looking up
    the value for $subkey in %key, and then using that value as a key to set
    the corresponding value in %hashname. Two completely different operations.
     
    Eric Bohlman, Jun 23, 2005
    #8
  9. Eric Bohlman <> wrote:
    > wrote in news:1119560161.977333.248710
    > @f14g2000cwb.googlegroups.com:
    >
    >> I know this structure is legal (well, it works). I extrapolated that
    >> 'bareword' could be replaced with a variable, hence it should work as
    >>
    >> $hashname{$key}{$subkey} = 'value';
    >>
    >> What is the functional difference between that and
    >>
    >> $hashname{$key{$subkey}} = 'value';
    >>
    >> ?

    >
    > In the first case, you have a hash (%hashname) and you're "subscripting" it
    > with two scalar variables, $key and $subkey. In the second case, you have
    > *two* hashes (%hashname and %key) and only one scalar ($subkey); you are
    > *not* making any use of a scalar called $key. Instead you're looking up
    > the value for $subkey in %key, and then using that value as a key to set
    > the corresponding value in %hashname. Two completely different operations.



    And the first one makes use of references, while there are no
    references involved with the second one.


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Jun 24, 2005
    #9
    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. Philipp
    Replies:
    21
    Views:
    1,181
    Philipp
    Jan 20, 2009
  2. Tore Aursand
    Replies:
    3
    Views:
    576
    Anno Siegel
    Sep 16, 2003
  3. Scott  Gilpin
    Replies:
    2
    Views:
    235
  4. Perl Learner

    Hashes of hashes or just one hash ?

    Perl Learner, Jun 8, 2005, in forum: Perl Misc
    Replies:
    11
    Views:
    230
  5. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    233
Loading...

Share This Page