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

I am trying to remove a hash from an array of hashes. I am generating my data structure like this.
push @successarray, { 'hour' => $hour, 'begintime' => $dbfirsttime, 'd +iff' => $diffstring, 'maxDD' => $maxDDL, 'maxDDTF' => $maxDDTF, 'time +frames' => $timeframe, 'maxtpTF' => $timeframe, 'maxtp' => $maxtp, 'y +dayopen' => $ydayopen, 'ydaydiff' => $ydaydiff};
I have tried the delete method. I realise its is deprectated.
for $i ( 0 .. $#successarray ) { if ($successarray[$i]->{'ydayopen'} > $fromopenmove) { delete $successarray[$i]; } }
this seems to work but leaves undefined 'gaps' in my arrays e.g
{ hour=22 maxDDTF=2 maxtp=143 ydaydiff=182 ydayopen=-53 diff=-34 -14 - +2 1 -11 maxtpTF=13 maxDD=29 timeframes=7 begintime=2010-05-27 22:30:0 +0 } { hour=14 maxDDTF=6 maxtp=42 ydaydiff=182 ydayopen=-71 diff=1 -43 -23 +-13 maxtpTF=23 maxDD=34 timeframes=14 begintime=2010-05-28 14:00:00 } { } { } { } { } { hour=03 maxDDTF=1 maxtp=184 ydaydiff=124 ydayopen=-51 diff=2 1 -12 1 + 4 -28 -16 maxtpTF=48 maxDD=1 timeframes=2 begintime=2010-06-15 03:00 +:00 }
I have played with the splice method a little, as this seems a more suitable function, but i cant for the life of me work out the syntax. can anyone please help point me in the right direction? thanks
conal.

Replies are listed 'Best First'.
Re: removing a hash from an array of hashes
by moritz (Cardinal) on Dec 13, 2010 at 15:03 UTC
    You can approach the problem from a slightly different angle. Instead of deleting empty array references, you can collect all those that are non-empty:
    my @dense = grep %$_, @successarray;
Re: removing a hash from an array of hashes
by cdarke (Prior) on Dec 13, 2010 at 16:13 UTC
    The tricky thing about using splice and a for loop is that you are altering the length of the array within the loop, so you need to perform a little hackery:
    use strict; use warnings; use Data::Dumper; my @successarray; for my $value (182,1000,201,300,124,1000,201,300,124) { push @successarray, {ydayopen => $value} } my $fromopenmove = 200; for (my $i = 0;$i < @successarray;$i++) { if ($successarray[$i]->{'ydayopen'} > $fromopenmove) { splice(@successarray,$i,1); $i--; # This is the part that you might not think of } } print Dumper(\@successarray);

      Another way to do that safely is to process the array backward:

      @x = 1..10;; for my $i ( reverse 0 .. $#x ){ $x[ $i ] & 1 and splice @x, $i, 1; };; print @x;; 2 4 6 8 10

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
      yeah. It was the decrement of the $i index count that i had overlooked in my experiments with splice

      thanks for taking the time to help me along.

      conal.
Re: removing a hash from an array of hashes
by Marshall (Canon) on Dec 13, 2010 at 23:28 UTC
    The easiest way to do what you want in Perl is to use grep{}. Think grep whenever the problem is to filter something - i.e. select a subset of data from a larger set or replace a data set with a subset of itself.

    Perl grep is way more powerful than command line grep! Instead of a for loop and some if logic, we have just a single statement as shown below.

    @success_array is an array of hash references. Each hash reference enters the grep. If whatever code that is within the grep evaluates to "true", then the hash reference is passed to the output (goes to the left). The code within the grep{} can be any valid Perl statements/conditions that you want. Here it is just a single statement that represents an "if". Note that it is completely legal to assign an array back to itself.

    Besides the obvious benefit of being shorter, another benefit is that this avoids all this messing around with subscripts! "off by one" array errors are among the most common problems in programming and many Perl iterators, idioms and techniques are designed to eliminate the need for subscripts. As you write more code the seemingly ubiquitous index "i" in other languages will appear less and less often in your Perl code.

    Sometimes there are performance reasons to do something more complicated, but usually the best way is to go with the idiomatic solution.

    #!/usr/bin/perl -w use strict; use Data::Dumper; my @success_array = ( { hour=>22, maxDDTF=>2, ydayopen=> -53 }, { hour=>14, maxDDTF=>6, ydayopen=> -71 }, { hour=>03, maxDDTF=>1, ydayopen=> -51}, ); # removes hash references where hash's key of 'ydayopen' is <= -60 + @success_array = grep { $_->{ydayopen} > -60 } @success_array; print Dumper \@success_array; __END__ $VAR1 = [ { 'hour' => 22, 'maxDDTF' => 2, 'ydayopen' => -53 }, { 'hour' => 3, 'maxDDTF' => 1, 'ydayopen' => -51 } ];