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

Greetings,

I have a default array of hashes. I replace the defaults, if they exist, with an arrayref. Is there a more efficient way to do this?

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; # Defaults my @t = ( { 'label' => 'kept', 'value' => '0', }, { 'label' => 'notkept', 'value' => '0', }, { 'label' => 'repaired', 'value' => '0', }, ); # Get this from another sub my $a = [ [ 'kept', '1', ], [ 'repaired', '3' ] ]; # Overwrite defaults with arrayref for my $i ( @{ $a } ) { for my $t ( @t ) { if ( $i->[0] eq $t->{label} ) { $t->{value} = $i->[1]; } } } warn Dumper( \$a ); warn Dumper( \@t );

Produces:

$VAR1 = \[ [ 'kept', '1' ], [ 'repaired', '3' ] ]; $VAR1 = [ { 'value' => '1', 'label' => 'kept' }, { 'value' => '0', 'label' => 'notkept' }, { 'label' => 'repaired', 'value' => '3' } ];

Neil Watson
watson-wilson.ca

Replies are listed 'Best First'.
Re: Convert arrayref to AoH
by Athanasius (Archbishop) on Sep 20, 2014 at 14:21 UTC

    Hello neilwatson,

    If your data is really always in the form shown, then, as Laurent_R says, you can simplify your data structure to a plain hash and overwrite default values easily:

    #! perl use strict; use warnings; use Data::Dump; my %h = map { $_ => 0 } qw(kept notkept repaired); my $a = [ [ 'kept', '1', ], [ 'repaired', '3', ], ]; dd \%h; %h = (%h, map { $_->[0] => $_ ->[1] } @$a); dd \%h;

    Output:

    0:16 >perl 1022_SoPW.pl { kept => 0, notkept => 0, repaired => 0 } { kept => 1, notkept => 0, repaired => 3 } 0:16 >

    But if (as I suspect) you really do need the more complex AoH, you can still make the overwriting code more efficient by breaking out of the inner loop when a match is found:

    # Overwrite defaults with arrayref OUTER: for my $i (@{$a}) { for my $t (@t) { if ($i->[0] eq $t->{label}) { $t->{value} = $i->[1]; next OUTER; } } }

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      If data organization can be changed, update can be made simpler and (slightly, perhaps even detectably) faster by changing update array element dereference:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -e "my %h = map { $_ => 0 } qw(kept notkept repaired); my $a = [ [ 'kept', '1', ], [ 'repaired', '3', ], ]; ;; dd \%h; %h = (%h, map @$_, @$a); dd \%h; " { kept => 0, notkept => 0, repaired => 0 } { kept => 1, notkept => 0, repaired => 3 }

      OTOH, using a for-loop to avoid creation of intermediate temp arrays might be yet simpler and even faster, although again, whether any speed gain would be significant or even detectable is an open question:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -e "my %h = map { $_ => 0 } qw(kept notkept repaired); my $a = [ [ 'kept', '1', ], [ 'repaired', '3', ], ]; ;; dd \%h; $h{ $_->[0] } = $_->[1] for @$a; dd \%h; " { kept => 0, notkept => 0, repaired => 0 } { kept => 1, notkept => 0, repaired => 3 }

Re: Convert arrayref to AoH
by Laurent_R (Canon) on Sep 20, 2014 at 14:20 UTC
    Can't you use a more adapted data structure giving you direct access to what you need as, for example, a simple hash? Something like this:
    my %t = ( kept => 0, notkept => 0, repaired => 0, );
Re: Convert arrayref to AoH
by Laurent_R (Canon) on Sep 21, 2014 at 09:35 UTC
    Hmm, thinking more about your problem, assuming you really need your array of hashes for some other reasons than what you have shown us, and also assuming that your labels are unique, you could build an auxiliary hash of hashes providing direct access to the values that you want to update, through the magics of references, i.e. store references to the data items that you need to change in an additional hash.
    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; # Defaults my @t = ( { 'label' => 'kept', 'value' => '0', }, { 'label' => 'notkept', 'value' => '0', }, { 'label' => 'repaired', 'value' => '0', }, ); # Create another structure (hash) to access the same data in a faster +way my %faster_access; for my $valref (@t) { $faster_access{$valref->{'label'}} = $valref; } # Get this from another sub my $a = [ [ 'kept', '1', ], [ 'repaired', '3' ] ]; # Overwrite defaults for my $i ( @{ $a } ) { $faster_access{$i->[0]}{value} = $i->[1]; } warn Dumper( \$a ); warn Dumper( \@t );
    The auxiliary HoH makes it possible to access directly (and much faster) to the data that you want to update. This is the output of the program:
    $ perl defaults.pl $ perl defaults.pl $VAR1 = \[ [ 'kept', '1' ], [ 'repaired', '3' ] ]; $VAR1 = [ { 'value' => '1', 'label' => 'kept' }, { 'value' => '0', 'label' => 'notkept' }, { 'value' => '3', 'label' => 'repaired' } ];
    As you can see, the @t array has been modified indirectly thanks to the changes made to the %faster_access containing references to the values that you need to update. There is no longer a need to browse through the whole AoH when you want to update it, which is handy if you need to update many times in the course of your program execution.