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

I have a selectall_hashref. I cannot change this. I must work with it. I am trying to reference keys within the returned hash reference. So far, I have been using nested foreach loops, but it just looks sloppy. I was hoping there was a better answer to this:
my $asdf = $dbh->selectall_hashref($sth, ['blah1','blah2','blah3']); my ($key1, $key2, $key3); foreach $key1 (keys %{$asdf}) { foreach $key2 (keys %{$asdf->{$key1}}) { foreach $key3 (keys %{$asdf->{$key1}->{$key2}} +) { # Do something here... } } }
blah1, blah2, and blah3 are column names in the $sth select. The values of those columns are key1, key2, and key3 respectively. Is there a way that I can directly reference the values of those column names (after the selectall_hashref) and ditch the foreach loops?

Replies are listed 'Best First'.
Re: DBI's selectall_hashref and nested foreach loops
by gube (Parson) on Jul 06, 2005 at 00:57 UTC
Re: DBI's selectall_hashref and nested foreach loops
by runrig (Abbot) on Jul 06, 2005 at 03:54 UTC
    You can clean that up a bit:
    my $asdf = $dbh->selectall_hashref($sth,['blah1','blah2','blah3']); while ( my ($key1, $val1) = each %$asdf ) { while ( my ($key2, $val2) = each %$val1 ) { while ( my ($key3, $val3 = each %$val2 ) { # Do something here... } } }
    And even when you use for/foreach loops, it's better to declare loop variables in the loop rather than before it, e.g.:
    for my $key ( keys %$hashref ) { ... }
Re: DBI's selectall_hashref and nested foreach loops
by anonymized user 468275 (Curate) on Jul 06, 2005 at 11:14 UTC
    To avoid having one foreach per level in the hash, I would write a recursive hash traversal subroutine as in this (tested) example, which uses a single foreach in a subroutine that calls itself when it finds a hash reference instead of a value:

    Update 1: the point of all this is not how many times foreach is actually executed but to avoid having to code differently for different nesting levels of the hash.

    Update 2: modified to show how to make the actual processing of the hash flexible. The assignment into a flat array is just to demonstrate that all the values and none of the keys of the hash were covered. A slight change can make it do something else at each node.

    Update 3: where the code examines ref($val), it could also be adapted to process arrays within hashes, by creating an extra branch for where ref($val) eq 'ARRAY'.

    #!/usr/bin/perl use strict; use Data::Dumper; my %h = (); # stick in some data at varying depths $h{ 1}{ 2}{ 3} = 123; $h{1}{3} = 13; $h{2} = 2; # this array will collect the values my @flat = (); hashTraverse( \%h, \@flat ); print Dumper( \@flat ); sub hashTraverse { my ( $href, $aref ) = ( shift(), shift() ); foreach my $key ( sort keys %$href ) { # this simple example sorts + the tree left-right my $val = $href -> { $key }; if ( ref( $val ) eq 'HASH' ) { hashTraverse( $href -> { $key }, $aref ) } else { push @$aref, $val; # or instead whatever else should be done to or with the h +ash node } } }

    Output:

    $VAR1 = [ 123, 13, 2 ];

    One world, one people