Building HoHoH from arbitrary size arrays ?

Discussion in 'Perl Misc' started by Allan Houston, Jul 12, 2005.

  1. Hi,

    I've searched and read about everything I can find on trying to do this,
    but my only solution I can think of is real, real ugly..

    Basically I'm looking to create a sub routine which will query a
    database table for the specified rows, and create a hash where each
    column is a key in the hash of hashes.

    i.e. From a table like so :

    +---------------+---------------+------------+------+
    | region | location | function | type |
    +---------------+---------------+------------+------+
    | Aztec West | Aztec West | PE Routers | 7513 |
    | Aztec West | Staverton | PE Routers | 7513 |
    | Barnsley | Barnsley | PE Routers | 7513 |
    | Barnsley | Bradford | PE Routers | 7513 |
    | Barnsley | Sheffield | PE Routers | 7513 |
    | Basildon | Basildon | PE Routers | 7513 |
    | Basildon | Gillingham | PE Routers | 7513 |
    -----------------------------------------------------

    I'd produce a hash like :

    $hash{'Aztec West'}{'Aztec West'}{'PE Routers}{'7513'}
    $hash{'Aztec West'}{'Staverton'}{'PE Routers}{'7513'}
    $hash{'Barnsley'}{'Barnsley'}{'PE Routers'}{'7513'} etc etc.

    The problem being that I'm trying to create a function which will read
    in say the first $count columns and return a hash of hashes with a depth
    of $count keys.

    The only way I can think to do it so far is something like this :

    ------------------------------------------------------------------------

    while (@t=$query->fetchrow_array())
    {

    if (@t == 1)
    { $hash{$t[0]}=''; }
    elsif (@t == 2)
    { $hash{$t[0]}{$t[1]}='';}
    elsif (@t == 3)
    { $hash{$t[0]}{$t[1]}{$t[2]}='';}
    elsif (@t == 4)
    { $hash{$t[0]}{$t[1]}{$t[2]}{$t[3]}='';}
    elsif (@t == 5)
    { $hash{$t[0]}{$t[1]}{$t[2]}{$t[3]}{$t[4]}='';}
    elsif (@t == 6)
    { $hash{$t[0]}{$t[1]}{$t[2]}{$t[3]}{$t[4]}{$t[5]}='';}

    }

    return(\%hash);

    ------------------------------------------------------------------------

    There has to be a more elegant solution than this surely.. Can anyone
    point me in the right direction please ?

    Cheers,
    Allan.
     
    Allan Houston, Jul 12, 2005
    #1
    1. Advertising

  2. Allan Houston

    Paul Lalli Guest

    Allan Houston wrote:
    > I've searched and read about everything I can find on trying to do this,
    > but my only solution I can think of is real, real ugly..
    >
    > Basically I'm looking to create a sub routine which will query a
    > database table for the specified rows, and create a hash where each
    > column is a key in the hash of hashes.

    <snip>
    > The problem being that I'm trying to create a function which will read
    > in say the first $count columns and return a hash of hashes with a depth
    > of $count keys.


    I strongly suspect an AB problem here. Why exactly do you feel the
    need to create such a function? What is your *actual* goal?

    > There has to be a more elegant solution than this surely.. Can anyone
    > point me in the right direction please ?


    I don't understand *why* you'd want to do this, but I believe this
    meets your criteria:

    #!/usr/bin/perl
    use strict;
    use warnings;
    use Data::Dumper;

    my @stuff = qw/foo bar baz bam boom/;

    my %h;
    my $ref = \%h;
    for my $level (@stuff){
    $ref->{$level} = {};
    $ref = $ref->{$level};
    }

    print Dumper(\%h);

    __END__


    $VAR1 = {
    'foo' => {
    'bar' => {
    'baz' => {
    'bam' => {
    'boom' => {}
    }
    }
    }
    }
    };




    Paul Lalli
     
    Paul Lalli, Jul 12, 2005
    #2
    1. Advertising

  3. Paul Lalli wrote:
    > Allan Houston wrote:
    >
    >>I've searched and read about everything I can find on trying to do this,
    >>but my only solution I can think of is real, real ugly..
    >>
    >>Basically I'm looking to create a sub routine which will query a
    >>database table for the specified rows, and create a hash where each
    >>column is a key in the hash of hashes.

    >
    > <snip>
    >
    >>The problem being that I'm trying to create a function which will read
    >>in say the first $count columns and return a hash of hashes with a depth
    >>of $count keys.

    >
    >
    > I strongly suspect an AB problem here. Why exactly do you feel the
    > need to create such a function? What is your *actual* goal?
    >
    >
    >>There has to be a more elegant solution than this surely.. Can anyone
    >>point me in the right direction please ?

    >
    >
    > I don't understand *why* you'd want to do this, but I believe this
    > meets your criteria:
    >
    > #!/usr/bin/perl
    > use strict;
    > use warnings;
    > use Data::Dumper;
    >
    > my @stuff = qw/foo bar baz bam boom/;
    >
    > my %h;
    > my $ref = \%h;
    > for my $level (@stuff){
    > $ref->{$level} = {};
    > $ref = $ref->{$level};
    > }
    >
    > print Dumper(\%h);
    >
    > __END__
    >
    >
    > $VAR1 = {
    > 'foo' => {
    > 'bar' => {
    > 'baz' => {
    > 'bam' => {
    > 'boom' => {}
    > }
    > }
    > }
    > }
    > };
    >
    >
    >
    >
    > Paul Lalli
    >


    Hi Paul,

    First off - thanks for your code, its certainly helped me.

    The reason I'm doing it this way is that the DBI fetchrow_hashref will
    overwrite values where the key has multiple values which is how my data
    is stored (unfortunately..)

    The sub I'm writing allows me to specify an array of arbitrary columns
    and get them returned in a hash hierarchy, which I find useful for
    getting at the data quickly and easily.

    Far more importantly however, you've shown me how to do something in
    perl which I didn't know before - thank you :)

    I made some minor changes to the code you provided to cope with the
    multiple key values :


    #!/usr/bin/perl
    use strict;
    use warnings;
    use Data::Dumper;

    my @stuff = ([qw/foo bar baz bam boom/],[qw/foo bar wizz wham whallop/]);

    my %h;
    my $ref = \%h;

    foreach my $stuff_ref (@stuff) {
    for my $level (@$stuff_ref) {

    if (exists $ref->{$level}) { # If the key exists then just move on

    $ref = $ref->{$level};
    next;
    }

    $ref->{$level} = {};
    $ref = $ref->{$level};
    }

    $ref = \%h; # Start at the beginning again

    }

    print Dumper(\%h);

    Which seems to work OK. I probably kludged up your beautiful code -
    please accept my humble apologies if this is the case.

    # ./hashes.pl
    $VAR1 = {
    'foo' => {
    'bar' => {
    'baz' => {
    'bam' => {
    'boom' => {}
    }
    },
    'wizz' => {
    'wham' => {
    'whallop' => {}
    }
    }
    }
    }
    };


    Cheers,
    Allan.
     
    Allan Houston, Jul 12, 2005
    #3
  4. Allan Houston

    Paul Lalli Guest

    Allan Houston wrote:
    > Paul Lalli wrote:
    > > Allan Houston wrote:
    > >
    > >>I've searched and read about everything I can find on trying to do this,
    > >>but my only solution I can think of is real, real ugly..
    > >>
    > >>Basically I'm looking to create a sub routine which will query a
    > >>database table for the specified rows, and create a hash where each
    > >>column is a key in the hash of hashes.

    > >

    >
    >
    > The reason I'm doing it this way is that the DBI fetchrow_hashref will
    > overwrite values where the key has multiple values which is how my data
    > is stored (unfortunately..)


    Out of curiousity, have you looked at the fetchall_hashref method?
    That one reads the entire table, and allows you to specify multiple key
    columns.

    > The sub I'm writing allows me to specify an array of arbitrary columns
    > and get them returned in a hash hierarchy, which I find useful for
    > getting at the data quickly and easily.
    >
    > Far more importantly however, you've shown me how to do something in
    > perl which I didn't know before - thank you :)
    >
    > I made some minor changes to the code you provided to cope with the
    > multiple key values :
    >
    >
    > #!/usr/bin/perl
    > use strict;
    > use warnings;
    > use Data::Dumper;
    >
    > my @stuff = ([qw/foo bar baz bam boom/],[qw/foo bar wizz wham whallop/]);
    >
    > my %h;
    > my $ref = \%h;
    >
    > foreach my $stuff_ref (@stuff) {
    > for my $level (@$stuff_ref) {
    >
    > if (exists $ref->{$level}) { # If the key exists then just move on
    >
    > $ref = $ref->{$level};
    > next;
    > }
    >
    > $ref->{$level} = {};
    > $ref = $ref->{$level};
    > }
    >
    > $ref = \%h; # Start at the beginning again
    >
    > }
    >
    > print Dumper(\%h);
    >
    > Which seems to work OK. I probably kludged up your beautiful code -
    > please accept my humble apologies if this is the case.


    I would just add a statement modifier to the body of the for loop,
    rather than the if block and duplicate statements:
    for my $level (@stuff){
    $ref->{$level} = {} unless exists $ref->{$level};
    $ref = $ref->{$level};
    }

    Paul Lalli
     
    Paul Lalli, Jul 12, 2005
    #4
  5. Allan Houston

    Paul Lalli Guest

    Paul Lalli wrote:
    > I strongly suspect an AB problem here.


    .... did I really say that? Sheesh.

    s/AB/XY/;


    Paul Lalli
     
    Paul Lalli, Jul 12, 2005
    #5
  6. Paul Lalli wrote:

    > for my $level (@stuff){
    > $ref->{$level} = {} unless exists $ref->{$level};
    > $ref = $ref->{$level};
    > }


    When building such a HoHoH there's no way that $ref->{$level} will
    exist but be false. As such you don't need the exists() in there...

    $ref->{$level} = {} unless $ref->{$level};

    ....which, of course, simlifies to...

    $ref->{$level} ||= {};

    ....which in turn makes it easy to combine it with the next statement...

    $ref = $ref->{$level} ||= {};

    ....although I tend to use autovivifivation instead...

    $ref = \%{$ref->{$level}};

    And given a short single statement I tend to opt for the statement
    qualifier version of for.

    $ref = \%{$ref->{$_}} for @stuff;

    In practice you often don't want the bottom level of such a HoHoH to be
    an empty hash so I usually use a refererence to reference instead.

    my $ref = \\%h;
    $ref = \$$ref->{$_} for @stuff;
     
    Brian McCauley, Jul 13, 2005
    #6
    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. Honestmath
    Replies:
    5
    Views:
    591
    Honestmath
    Dec 13, 2004
  2. Wilbert Berendsen

    building arbitrary files with distutils?

    Wilbert Berendsen, Mar 10, 2008, in forum: Python
    Replies:
    1
    Views:
    239
    Martin v. Löwis
    Mar 10, 2008
  3. Philipp
    Replies:
    21
    Views:
    1,190
    Philipp
    Jan 20, 2009
  4. Replies:
    5
    Views:
    198
    Roger Pack
    Feb 5, 2009
  5. Aaron DeLoach

    HoHoH (hash of HoH's)

    Aaron DeLoach, Jul 18, 2004, in forum: Perl Misc
    Replies:
    10
    Views:
    185
    Aaron DeLoach
    Jul 20, 2004
Loading...

Share This Page