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

Hi Monks. Can someone explain why this works:

use strict; use warnings; my @data; push @data, { name => "Item A", price => 9.99 }; push @data, { name => "Item B", price => 4.99 }; push @data, { name => "Item C", price => 7.5}; my @sorted = sort {$a->{price} <=> $b->{price}} @data; print join "\n", map {$_->{name}." - ".$_->{price}} @sorted;

and this doesn't:

use strict; use warnings; my @data; my %recordset; $recordset{name} = "Item A"; $recordset{price} = 9.99; push @data, \%recordset; $recordset{name} = "Item B"; $recordset{price} = 4.99; push @data, \%recordset; $recordset{name} = "Item C"; $recordset{price} = 7.5; push @data, \%recordset; my @sorted = sort {$a->{price} <=> $b->{price}} @data; print join "\n", map {$_->{name}." - ".$_->{price}} @sorted;

the first one outputs this:

Item B - 4.99
Item C - 7.5
Item A - 9.99


And the second outputs this:

Item C - 7.5
Item C - 7.5
Item C - 7.5

Replies are listed 'Best First'.
Re: Newbie hash/sorting question
by Eliya (Vicar) on Dec 09, 2011 at 03:39 UTC

    In the first case, you push three different records (anonymous hashes).

    In the second case, you have one %recordset, a reference to which you push three times. The values you've assigned last to this recordset show up in all three items, simply because they're one and the same record.

Re: Newbie hash/sorting question
by NetWallah (Canon) on Dec 09, 2011 at 04:07 UTC
    Expanding on what Eliya said:

    One way to correct this (not the best, but the simplest) is to make a COPY of the %recordset each time.

    That way, you get a reference to the CONTENTS at the current time, instead of the same reference.

    Change your "push" statements to :

    push @data, {%recordset}; # Make a reference to a COPY of the contents + of %recordset

                "XML is like violence: if it doesn't solve your problem, use more."

      push @data, {%recordset};

      That is probably a bit inefficient, as AFAIK it copies the hash to create the anonymous hash reference. Something like this would work:

      my $recordset = {}; $recordset->{name} = "Item A"; $recordset->{price} = 9.99; push @data, $recordset; # start with a new hash reference $recordset = {}; $recordset->{name} = "Item B"; $recordset->{price} = 4.99; push @data, $recordset;

      Of course, declaring my $recordset; inside the loop where you fill @data is probably the nicest approach.

        Thanks all... I'm starting to get the gist of it. So let me make sure I get this straight. If I did the same thing as my example #2 but put it in a loop, then I do get the distinct records in the array. in other words, this example works fine:

        use strict; use warnings; use Data::Dumper; my @data; for (my $i=5, my $j=0; $i<=10, $j<=20 ; $i++, $j++) { my %recordset; $recordset{name} = $i; $recordset{price} = $j; push @data, \%recordset; } print Dumper(\@data);

        Outside of a loop, this wouldn't work and the push would overwrite what is already there. yes?

Re: Newbie hash/sorting question
by RichardK (Parson) on Dec 09, 2011 at 12:03 UTC

    You can use Data::Dumper to see the contents of you array and understand what's happening.

    Something like:-

    use Data::Dumper; my @set; # .... print Dumper(\@set);
Re: Newbie hash/sorting question
by pvaldes (Chaplain) on Dec 09, 2011 at 09:54 UTC
    $recordset{"Item B"} = "4.99"; $recordset{"Item C"} = "7.5";