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

I'm trying to convert an AoA returned by DBI->fetchall_arrayref into a HoA.

My AoA returned by fetchall_arrayref looks like this:
$VAR1 = [ [ '95', undef ], [ '100', '0' ] [ '105', '1' ], [ '110', '1' ] ];
I want to take the second element in each array and make it a hash key. If the second element is undef, then I want to change it to '0'. Also I want to take the first element in every array and push it into the array referenced by the hash ( which is the second element in the array).

Here's my try at a test script to do this, what's wrong with it? I see that there's something wrong with pushing to an array slice.
!/usr/bin/perl -w use strict; use Data::Dumper; my @one = qw(95 undef); my @two = qw(100 2); my @three = qw(105 undef); my @four = qw(100 2); my @array = \@one; push @array, \@two; push @array, \@three; push @array, \@four; print Dumper @array; my %hash; foreach my $ary ( @array ) { if ( @$ary[1] eq undef ) { @$ary[1] = '0' } $hash{@$ary[1]} = push @$ary[0]; } print Dumper %hash;

Replies are listed 'Best First'.
Re: Transform Array Ref to Hash Ref - Where's my error?
by Errto (Vicar) on Feb 14, 2006 at 01:31 UTC
    What you've got there is pretty close, but you've got the syntax for the push part a bit backwards. With push, you always specify the array to be pushed onto first, and you don't use the assignment operator:
    my %hash; foreach my $ary ( @array ) { if ( !defined($$ary[1]) ) { $$ary[1] = '0' } push @{$hash{$$ary[1]}}, $$ary[0]; } print Dumper \%hash;
    I also changed your @$ usage to $$. @$ary[1] is an array slice, which is not appropriate to use when you are only dealing with single elements, as is the case here. I also introduced the defined function, which is the only valid way to check whether a value is defined or not (string comparison to undef will succeed for the empty string '' and throw a warning).

    By the way, as a personal preference I like to use -> for single element lookups in hash or array references because I think it is clearer. So everywhere you have $$ary[1] you can instead say $ary->[1].

    Forgot one other thing. I changed print Dumper %hash to print Dumper \%hash because otherwise Dumper will treat your hash as simply a list of values and not as an actual hash.

    Yet one more. I aligned the two statements inside the foreach loop because they are in fact part of the same block. In the OP a reader might think that push statement was part of the if block, which it's not.

      No need (and probably shouldn't ... though i see that OP did) to modify the input w/the $$ary[1] = '0' statement ..
      my %hash; foreach my $row ( @array ) { my $k = defined($row->[1]) ? $row->[1] : 0; push @{ $hash{$k} }, $row->[0]; }
      or a more condensed version:
      my %hash; push @{ hash{ defined($_->[1]) ? $_->[1] : 0 } }, $_->[0] for @array;
        Even more condensed:
        my %h; push @{$h{$_->[1]||0}}, $_->[0] for @a;
Re: Transform Array Ref to Hash Ref - Where's my error?
by McDarren (Abbot) on Feb 14, 2006 at 01:12 UTC
    I want to take the second element in each array and make it a hash key.
    That's not going to work, because hash keys must be unique. Well, it will "work", but you're going to clobber a lot of your data.