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

I am a real noob at perl, though I know some other languages so I am not completely lost. Or well, now I am. I have an hash-array filled with about 500 entries. I am looping through them and removing some dupes like this:
for ($i=0; $i<=$#players; $i++) { my %player = %{$players[$i]}; for ($ii=0;$ii<=$#players;$ii++) { if ($i != $ii) { my %player2 = %{$players[$ii]}; if ($player{name} eq $player2{name}) { $player{deaths} += $player2{deaths}; $player{kills} += $player2{kills}; #Remove the dupe entry splice(@players, $ii, 1); } } } }
But when I am printing those new values later on in a loop, like this:
my %player = %{$players[$i]}; print "$player{kills}";
They print the old value. How do I save the values I changed them to before?

Replies are listed 'Best First'.
Re: Save hash value?
by chromatic (Archbishop) on Jun 05, 2011 at 22:40 UTC

    If you indexed your players by name when you created the array, you wouldn't have to perform this expensive duplicate checking.

      ...say whut? Would you mind explaining more about that? What I need to do, is merge all dupes(the data of the entries), not just remove them. Btw, Anonymous Monk, now I get what you mean and it worked ;) thx!

        I assume you create @players somehow. If you have a loop in which you do so (or if you can arrange your code to create some sort of loop), you could make a hash instead of an array and write:

        my %players; while (my $player = make_next_player()) { if (exists $players{ $player->{name} } { # merge new player data with existing player } else { $players{ $player->{name} } = $player; } }
Re: Save hash value?
by Anonymous Monk on Jun 05, 2011 at 18:57 UTC
    Don't make copies.
    use DDS; my $ref = { 1 .. 4 }; my %foo = %{ $ref }; $foo{1} = 6; Dump( $ref, \%foo ); __END__ $HASH1 = { 1 => 2, 3 => 4 }; $HASH2 = { 1 => 6, 3 => 4 };
    %foo and $ref point to two different hashes. %foo is a shallow copy of has pointed to by $ref. See references quick reference
Re: Save hash value?
by ig (Vicar) on Jun 06, 2011 at 02:42 UTC

    You have another problem... not only are you modifying copies of your data rather than the original, but you are also modifying the array you are iterating over. This often leads to unexpected results. Consider the following:

    use strict; use warnings; use Data::Dumper; my @array = (1..10); for (my $i = 0; $i <= $#array; $i++) { splice(@array, $i, 1); } print Dumper(\@array);
      I completely understand what you mean, great that you pointed it out.
Re: Save hash value?
by Marshall (Canon) on Jun 06, 2011 at 06:57 UTC
    Since you don't want to have names repeated. The easiest thing is to convert your array of hashes (AoH) into a hash of hash (HoH). The first part of the code translates your AoH into a HoH. Then I show how to add somebody new (mary) to the structure. You can use this way to create the structure from scratch. (e.g. no real need for the AoH structure unless you need it for some other reason).
    #!/usr/bin/perl -w use strict; use Data::Dump qw(pp); my @players = ( {name => 'Bob', deaths => 2, kills => 5}, {name => 'Bennie', deaths => 1, kills =>13}, {name => 'Bob', deaths => 10, kills => 10}, {name => 'Jane', deaths => 0, kills =>30} ); my %playerHoH; foreach my $href (@players) { my $name = $href->{name}; $playerHoH{$name}{deaths} += $href->{deaths}; $playerHoH{$name}{kills} += $href->{kills}; } # adding new player is easy... same case as adding deaths and # kills to an existing player. # $palyerHoH{name} will be created if it doesn't already exist. $playerHoH{mary}{deaths} += 4; $playerHoH{mary}{kills} += 20; print '%playerHOH = ',pp(\%playerHoH),"\n"; __END__ Prints: \%playerHOH = { Bennie => { deaths => 1, kills => 13 }, Bob => { deaths => 12, kills => 15 }, Jane => { deaths => 0, kills => 30 }, mary => { deaths => 4, kills => 20 }, }
    Update: for completeness, I added how to convert the HoH back to AoH. See below..

      Great code, but I don't understand how an HoH will prevent multiple entries with the same name? What I am doing is that I am reading a file with stats, and then I want to merge any doublets with the same name. Then I will write the new stats to a new file. I have got my code working atm, though I will be making optimizations to it from what is written here. To start with I will use chromatics solution to begin with :)
        I don't understand how an HoH will prevent multiple entries with the same name?
        In a plain hash, there are unique hash keys and each of them has an associated value e.g. Bob => 23. The key Bob must be unique (it can only appear once). When you have a Hash of Hash (HoH), instead of say Bob's value of 23, this is replaced by a reference to another hash. The main key "Bob" still has to be unique - nothing really changed except than instead of a numeric value of 23, I put a reference to another hash.

        I printed the HoH for you using the Data::Dump function pp()....

        my %HoH = ( Bennie => { deaths => 1, kills => 13 }, Bob => { deaths => 12, kills => 15 }, Jane => { deaths => 0, kills => 30 }, mary => { deaths => 4, kills => 20 }, );
        keys(%HoH) are: Bennie,Bob,Jane,mary (note case matters!). The value of those keys are references to anonymous hashes (hashes which have no specific name of their own). Each one of those hashes contains key/value pairs for deaths and kills. If you write:
        $playerHoH{Harry}{deaths} += 4; $playerHoH{Harry}{kills} += 20;
        It does not matter if Harry already exists or not. If he doesn't Perl makes him for you. The += causes any existing values to be added to the deaths or kills. In the case where Harry is "brand new", this still works because the new entry starts out at essentially a 0 numeric value for the purposes of doing the addition here.

        Maybe some more code will help...I show how to print the HoH manually using 2 different but equivalent syntax's..

        #!/usr/bin/perl -w use strict; my %playerHoH = ( Bennie => { deaths => 1, kills => 13 }, Bob => { deaths => 12, kills => 15 }, Jane => { deaths => 0, kills => 30 }, mary => { deaths => 4, kills => 20 }, ); $playerHoH{Harry}{deaths} += 4; $playerHoH{Harry}{kills} += 20; foreach my $name (keys %playerHoH) { print "$name "; #prints: Jane Bob Harry mary Bennie } print "\n"; #again Jane Bob mary Bennie must be and are unique. #the hash forces them to be that way foreach my $name (keys %playerHoH) { my $hash_ref = $playerHoH{$name}; #the value is a reference #to another hash! foreach my $key (keys %$hash_ref) { ### these 2 formulations are equivalent and print ### the same thing print "One way: $name: $key => $hash_ref->{$key}\n"; print "Another way: $name: $key => $playerHoH{$name}{$key}\n"; } print "\n"; } =above prints: One way: Jane: deaths => 0 Another way: Jane: deaths => 0 One way: Jane: kills => 30 Another way: Jane: kills => 30 One way: Bob: deaths => 12 Another way: Bob: deaths => 12 One way: Bob: kills => 15 Another way: Bob: kills => 15 One way: Harry: deaths => 4 Another way: Harry: deaths => 4 One way: Harry: kills => 20 Another way: Harry: kills => 20 One way: mary: deaths => 4 Another way: mary: deaths => 4 One way: mary: kills => 20 Another way: mary: kills => 20 One way: Bennie: deaths => 1 Another way: Bennie: deaths => 1 One way: Bennie: kills => 13 Another way: Bennie: kills => 13 =cut
        I suggest looking at Some tutorials on hashes
Re: Save hash value?
by ww (Archbishop) on Jun 05, 2011 at 22:03 UTC

    OT, or at best, "tangential", but you really should make a practice of

    use strict; use warnings;

    and, when stumped despite their assistance,

    use diagnostics;