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

I'm an intermittent perl user and its been a while since I did anything with perl. I've put a high level overview of the problem i'm trying to solve at the end of this in case the detailed bit doesnt make sense.

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

I'm using selectall_arrayref from DBI to get some data from a database.

I want to use the data returned in the query, in sequence, to populate or adjust the values in a hash. I was thinking I could iterate over the array and do what I need to do to the values in the hash.

I can access the data in the arrayref returned by the query by using @{$months->[$x]}, but when I try to use @{$months->[$x]} as the key for the hash, I get nothing - $monthhash{ @{$months->[$x]} } which I dont understand.

code below ---------------------
my $months = $dbh->selectall_arrayref("select to_char(last_day(add_mon +ths('14-May-2004', (rownum-1))),'MON-RRRR') dt from all_objects where rownum <= months_between(sysdate,'31-May-2004')+1"); my $month; my %monthhash; my $x = 0; foreach $month (@$months) { $monthhash{@$month[0]} = 0; print "check $monthhash{@$month[0]} @$month[0] @{$months->[$x]} ** + $monthhash{ (@{$months->[$x]}) }\n"; $x++; }

sample output of print

check 0 MAY-2004 MAY-2004 **

High level description.

I have a sequence of months (think of them as the columns on a spreadsheet) and I will have numbers for some of them but not others. I need to make sure that any gaps are filled with the most recent number; for example. I have values for May, Aug & Sep and I need to fill in the blanks. So my thinking is to populate the data I know about and then cycle through the months from left to right and where I find a blank I take the preceding value and replace the blank then move on.

Original
May 04 Jun 04 Jul 04 Aug 04 Sep 04 Oct 04 50 100 110 May 04 Jun 04 Jul 04 Aug 04 Sep 04 Oct 04 50 50 50 100 110 110

There's bound to be several ways of doing this, so please feel free to suggest things, bear in mind that this is the simple version, I will have to be doing this for data where there will be a fair bit of manipulation going on before the numbers are populated.

Thanks in advance

Edit: g0n - extra code tags

Replies are listed 'Best First'.
Re: Using array value (from array ref) as hash key??
by ikegami (Patriarch) on May 25, 2007 at 05:20 UTC

    Let's try that again. I was on the right track in my first post, but I took a wrong turn.

    The problem is with
    $monthhash{ @{$months->[$x]} }

    $months->[$x]
    returns a reference to a array of values (one for each field).

    When evaluated in scalar context,
    @{$months->[$x]}
    returns the number of elements in the dereferenced array. In this case, that means it returns the number of fields (1).

    $monthhash{ @{$months->[$x]} }
    is therefore the same as
    $monthhash{1}

    You wanted
    $monthhash{ $months->[$x][0] }
    Remember, you are dealing with an Array (records) of Array (fields).

      Thanks for the reply and the clarification, I understand what you're saying now. :)

      I remembered something I had problems with before and tried that and now I'm even more puzzled...(I've had problems with things not working due to whitespace issues so I fairly routinely use a trim function I got from the cookbook), I tried it with this problem and lo and behold it works.

      my $months = $dbh->selectall_arrayref("select to_char(last_day(add_mon +ths('14-May-2004', (rownum-1))),'MON-RRRR') dt from all_objects where rownum <= months_between(sysdate,'31-May-2004')+1"); my $month; my $monthhash; my $i = 0; # populate month hash with zero's for now (this may need to be done la +ter) foreach $month (@$months) { $monthhash{@$month[0]} = $i; # print "pop @$month[0] $i\n"; $i++; } for($i=0; $i<$#$months; $i++) { print "hope 1 @{$months->[$i]}\n"; print "hope 2 $monthhash{trim(@{$months->[$i]})}\n"; print "hope 2A $monthhash{@{$months->[$i]}}\n"; } sub trim { my @out = @_; for (@out) { s/^\s+//; #trim left s/\s+$//; #trim right } return @out == 1 ? $out[0] #only one to return : @out; #or many }

      Results. I added an incrementing value to the hash when I created it to make sure that I was indeed getting the correct value - As far as I can see the only difference between hope 2 & hope 2A is the trim call, yet one works and the other doesn't. I don't know why this should make any difference but it is now working.

      I was wondering whether it had anything to do with the array only having one element. I started with the simple query so I'll be using your notation later on. If thats not it, then I have no idea why the trim should make any difference.

      hope 1 MAY-2004 hope 2 0 hope 2A hope 1 JUN-2004 hope 2 1 hope 2A hope 1 JUL-2004 hope 2 2 hope 2A hope 1 AUG-2004 hope 2 3 hope 2A hope 1 SEP-2004 hope 2 4 hope 2A
        $key = @...[...]; <--- Wrong ^ ^ It may work because Perl is "smart", | | but Perl doesn't always guess correctly. +------+ $key = $...[...]; <--- Right ^ ^ | | +------+

        A hash key is a scalar, so you need a "$ expression".

        # Wrong: print "hope 1 @{$months->[$i]}\n"; print "hope 2 $monthhash{trim(@{$months->[$i]})}\n"; print "hope 2A $monthhash{@{$months->[$i]}}\n"; # Right: print "hope 1 $months->[$i][0]\n"; print "hope 2 $monthhash{trim($months->[$i][0])}\n"; print "hope 2A $monthhash{$months->[$i][0]}\n";

        You're assuming an array of one element (@array) is interchangeable with the element ($array[0]). That's not always the case. In fact, you should assume it's not the case.

        By the way, @out == 1 is a bug. You should replace @out == 1 with wantarray. You'll get some interesting results when you do.

Re: Using array value (from array ref) as hash key??
by ikegami (Patriarch) on May 25, 2007 at 05:01 UTC

    Hash keys are evaluated in scalar context. @something (including @{expr}) returns the number of elements in @something when evaluated in scalar context.

    $monthhash{@$month[0]}
    should be
    $monthhash{$month->[0]}
    if you want the first element of @$month to be used as the key.

    By the way,
    $month->[0]
    can also be written as
    ${$month}[0]
    and
    $$month[0]
    but they're less readable.

    Update: Oops! Array slices, such as @something[0] and @{expr}[0] do *not* return a number of elements in scalar context. You're needlessly using array slices, but that's not your problem. While $monthhash{$month->[0]} would be better, it won't fix anything.

Re: Using array value (from array ref) as hash key??
by blazar (Canon) on May 25, 2007 at 11:24 UTC
    I can access the data in the arrayref returned by the query by using @{$months->$x}, but when I try to use @{$months->$x} as the key for the hash, I get nothing - $monthhash{ @{$months->$x} } which I dont understand.

    It has been extensively explained to you, but just to stress it: hash keys are strings. Thus you can't use an array as a key, whether it's an actual array or a dereference of an arrayref. You could use "@array" as a key, but generally there's no need to do that and I don't think it's what you have in mind. OTOH you can take a slice of an hash, but in that case that's

    @monthhash{ (@{$months->[$x]}) }

    not

    $monthhash{ (@{$months->[$x]}) }

    I'm more and more convinced of what I wrote!