Sort by hash vaule, an array of hash references

F

fahdsultan

I have an array of hash references like so:

$array = ( $hashref1, $hashref2, ... );

where $hashrefn = { id => 001,
name => username,
count => 2020, };

I'm trying to sort the array by the hash key 'count'.

I've tried:
my @array_sorted = sort { ($a->{count}) <=> ($b->{count}) } @array;
and various ways using map-sort-map, but the data come out the other
side unsorted.

Any insight into the problem would be appriciated.

thanks,

fahd
 
P

Paul Lalli

I have an array of hash references like so:

$array = ( $hashref1, $hashref2, ... );

There is no array in the above code.

Did you mean either:
$array = [ $hashref1, $hashref2, ... ];
or
@array = ($hashref1, $hashref2, ...);
?
where $hashrefn = { id => 001,
name => username,
count => 2020, };

I'm trying to sort the array by the hash key 'count'.

I've tried:
my @array_sorted = sort { ($a->{count}) <=> ($b->{count}) } @array;
and various ways using map-sort-map, but the data come out the other
side unsorted.

A quick glance at that code looks correct to me. I'm inclined to
believe you didn't define your array (or array reference) correctly.

Please post a short but *complete* program that we can copy and paste,
which demonstrates your error.

Paul Lalli
 
F

fahdsultan

Please post a short but *complete* program that we can copy and paste,
which demonstrates your error.

Here is part of it, I think that this is enough to be relevent


#!/usr/bin/perl

use strict;
use ........... code removed ............

my
($_RH_users_in_queue,$_RA_user_report_queue,$array_item,$keys,$start_date,
$end_date);
my ($_total_tickets,%_total_tickets, $i, $j, %_RH_users_in_queue,
$_user_resolved, $teamreport, @teamreport, $_norm_tickets);

.............. code removed ..............

foreach $i ('queue01','queue02','queue03' )
{
$_total_tickets = $query->total_tickets_queue($dbh,
$query->param(-name=>"$i"), $start_date, $end_date);
if ( ( $query->param(-name=>"$i") == 0 )
|| $_total_tickets == 0 ) { last }
$_RH_users_in_queue =
$query->users_in_queue($dbh,$query->param(-name=>"$i"));
$_RA_user_report_queue = $query->user_report_queue_resolved($dbh,
$query->param(-name=>"$i"), $_RH_users_in_queue, $start_date,
$end_date);
foreach $j (keys %$_RH_users_in_queue)
{
my %matrix;
$matrix{id} = $j;
$matrix{name} = $_RH_users_in_queue->{$j};
$matrix{queue} = $query->param(-name=>"$i");
$matrix{queue_total} = $_total_tickets;
$matrix{tickets} =
$query->user_tickets_resolved($dbh,$matrix{queue},$matrix{id},$start_date,$end_date);
$matrix{norm_tickets} = ( $matrix{tickets} / $_total_tickets ) *
100;
push(@teamreport ,\%matrix);

}

}


#my @teamreport_sorted = sort { $a{norm_tickets} <=> $b{norm_tickets}
} @teamreport;

my @teamreport_sorted = sort { ($a->{norm_tickets}) <=>
($b->{norm_tickets}) } @teamreport;

@teamreport_sorted when printed is unsorted.

thanks,

fahd
 
E

Eric Schwartz

Here is part of it, I think that this is enough to be relevent

I don't mean to be rude, but Paul clearly asked for something that is
short (about 20 lines is reasonably considered short), and most
importantly, cut-and-pasteable.

Here's what something like that might look like:

#!/usr/bin/perl
use warnings;
use strict;

my @unsorted = ( { id => 03, name => "hi", count=> 200 },
{ id => 01, name => "hi", count=> 100 },
{ id => 02, name => "hi", count=> 150 } );

my @sorted = sort { $a->{count} <=> $b->{count} } @unsorted;

print "id: [$_->{id}] name: [$_->{name}], count: [$_->{count}]\n" for @sorted;
__END__

This gives me:

id: [1] name: [hi], count: [100]
id: [2] name: [hi], count: [150]
id: [3] name: [hi], count: [200]

Observe the following characteristics, and how they differ from what
you offered:

1) My code is very short. It's easy to read. 20 lines is about the
maximum we recommend that people post, because longer than that,
code gets much harder to read.

Your code is about twice that long, and includes a lot of
extraneous information that a reader has to read and understand and
then decide if it's meaningful or not.

2) My code only contains the very bare minimum required to display the
problem (or in this case, lack of problem :).

Your code contains about 30 lines of code that doesn't actually
involve your problem; instead, it's a loop that iterates over data
we don't know anything about to create a structure that we can only
guess at.

Since you're not asking us for help in generating your hashrefs,
it's not relevant to your problem how your hashrefs are created.
For the purposes of sorting them, all we need is a few examples of
what they look like, and what your attempt to sort them looks like.

3) My code is cut-and-pasteable into a file. Including the __END__
token will allow anybody who accidentally includes a few extra
lines to run it anyway.

If your code cannot be cut and pasted, you lose the help a LOT of
very talented people who don't want to spend the time reading
through your code and trying to understand it. Code that runs is
very easy to look at and understand. Code that doesn't requires
very hard work to understand, and most of us are volunteers and not
inclined to doing that work.

4) I used strict (which you did, and good for you for doing so) AND
warnings. Posting code which uses both probably doubles if not
triples the number of people willing to help.
#!/usr/bin/perl

use strict;
use ........... code removed ............

This doesn't compile. That's why our posting guidelines (which are
posted on this newsgroup at least twice a week) specify to only post
code which is cut and pasted; that removes the possibility of typos
causing problems.
my
($_RH_users_in_queue,$_RA_user_report_queue,$array_item,$keys,$start_date,
$end_date);
my ($_total_tickets,%_total_tickets, $i, $j, %_RH_users_in_queue,
$_user_resolved, $teamreport, @teamreport, $_norm_tickets);

We don't need all this information to solve your problem.
............. code removed ..............

foreach $i ('queue01','queue02','queue03' )
{
$_total_tickets = $query->total_tickets_queue($dbh,
$query->param(-name=>"$i"), $start_date, $end_date);
if ( ( $query->param(-name=>"$i") == 0 )
|| $_total_tickets == 0 ) { last }
$_RH_users_in_queue =
$query->users_in_queue($dbh,$query->param(-name=>"$i"));
$_RA_user_report_queue = $query->user_report_queue_resolved($dbh,
$query->param(-name=>"$i"), $_RH_users_in_queue, $start_date,
$end_date);
foreach $j (keys %$_RH_users_in_queue)
{
my %matrix;
$matrix{id} = $j;
$matrix{name} = $_RH_users_in_queue->{$j};
$matrix{queue} = $query->param(-name=>"$i");
$matrix{queue_total} = $_total_tickets;
$matrix{tickets} =
$query->user_tickets_resolved($dbh,$matrix{queue},$matrix{id},$start_date,$end_date);
$matrix{norm_tickets} = ( $matrix{tickets} / $_total_tickets ) *
100;
push(@teamreport ,\%matrix);

}

}

All this code does is, it seems, build up a list of hashrefs.
Instead, you could just show us an example list, maybe three or four
items long. We just need enough information to know what you're
sorting, and what it looks like.
#my @teamreport_sorted = sort { $a{norm_tickets} <=> $b{norm_tickets}
} @teamreport;

This is a little confusing. I'd leave it out next time.
my @teamreport_sorted = sort { ($a->{norm_tickets}) <=>
($b->{norm_tickets}) } @teamreport;

This is the meat of the sort, and as far as I can tell, it's correct.
The parenthesis are unnecessary, though, and I'd remove them for
clarity:

my @teamreport_sorted = sort { $a->{norm_tickets} said:
@teamreport_sorted when printed is unsorted.

Please show us exactly what it looks like before, and exactly what it
looks like after. And no, this is NOT a request for every single one
of your 2,000 hashrefs; just give us a few, and show how you print
them out before, and how you print them out after; reduce the problem
to its barest possible essentials.

This is not, repeat NOT intended as a flame or rip on your skills.
I'm trying to help you help yourself. If you give us an example that
follows all 4 of the criteria I delineated above, you'll have a very
fast answer that is very high quality. And, better yet, in the
process of stripping things down into the smallest possible example,
you may even figure out the problem on your own, which is even better.

-=Eric
 
P

Paul Lalli

Here is part of it, I think that this is enough to be relevent

.... You don't see any contradiction between what I just asked for and
what you gave? If you don't want to follow my request geared towards
helping you help us help you, why should I bother attempting to help
you at all?

Paul Lalli
 
R

robic0

fahdsul, this could be your problem

%matrix = ();
push(@teamreport ,\%matrix);

is not the same as

$matrix = {};
push(@teamreport ,$matrix);


try:
my @teamreport_sorted = sort { ${$a}{norm_tickets} <=>
${$b}{norm_tickets} } @teamreport;
 
R

robic0

fahdsul, this could be your problem

%matrix = ();
push(@teamreport ,\%matrix);

is not the same as

$matrix = {};
push(@teamreport ,$matrix);


try:
my @teamreport_sorted = sort { ${$a}{norm_tickets} <=>
${$b}{norm_tickets} } @teamreport;

Test:
use strict;
use warnings;
use sort 'stable';

my @teamreport = ();
for (my $i = 0; $i < 4; $i++) {
my %matrix = ();
$matrix{'name'} = "n$i";
$matrix{'date'} = "d$i";
$matrix{'quantity'} = 50-(2*$i);
$matrix{'nothing'} = 'nothing';
push(@teamreport ,\%matrix);
}
my @teamreport_sorted = sort { ${$a}{'quantity'} <=> ${$b}{'quantity'}
} @teamreport;

for (@teamreport) {
print "name:\t${$_}{'name'}\tquantity:\t${$_}{'quantity'}\n";
}

print "======================\n";

for (@teamreport_sorted) {
print "name:\t${$_}{'name'}\tquantity:\t${$_}{'quantity'}\n";
}

'
D:\Eraser\esp>perl zzz.pl
name: n0 quantity: 50
name: n1 quantity: 48
name: n2 quantity: 46
name: n3 quantity: 44
======================
name: n3 quantity: 44
name: n2 quantity: 46
name: n1 quantity: 48
name: n0 quantity: 50

__END__

If however, %matrix were the same scope as @teamsport, only
each item in @teamsport references would all be the same
referencing last values asingned to %matrix.
 
R

robic0

Test:
use strict;
use warnings;
use sort 'stable';

my @teamreport = ();
for (my $i = 0; $i < 4; $i++) {
my %matrix = ();
$matrix{'name'} = "n$i";
$matrix{'date'} = "d$i";
$matrix{'quantity'} = 50-(2*$i);
$matrix{'nothing'} = 'nothing';
push(@teamreport ,\%matrix);
}
my @teamreport_sorted = sort { ${$a}{'quantity'} <=> ${$b}{'quantity'}
} @teamreport;

for (@teamreport) {
print "name:\t${$_}{'name'}\tquantity:\t${$_}{'quantity'}\n";
}

print "======================\n";

for (@teamreport_sorted) {
print "name:\t${$_}{'name'}\tquantity:\t${$_}{'quantity'}\n";
}

'
D:\Eraser\esp>perl zzz.pl
name: n0 quantity: 50
name: n1 quantity: 48
name: n2 quantity: 46
name: n3 quantity: 44
======================
name: n3 quantity: 44
name: n2 quantity: 46
name: n1 quantity: 48
name: n0 quantity: 50

__END__

If however, %matrix were the same scope as @teamsport, only
each item in @teamsport references would all be the same
referencing last values asingned to %matrix.

use strict;
use warnings;
use sort 'stable';

my @teamreport = ();
for (my $i = 0; $i < 4; $i++) {
my $matrix = {};
$matrix->{'name'} = "n$i";
$matrix->{'date'} = "d$i";
$matrix->{'quantity'} = 50-(2*$i);
$matrix->{'nothing'} = 'nothing';
push(@teamreport ,$matrix);
}
my @teamreport_sorted = sort { $a->{'quantity'} <=> $b->{'quantity'}
} @teamreport;

for (@teamreport) {
print "name:\t$_->{'name'}\tquantity:\t$_->{'quantity'}\n";
}

print "======================\n";

for (@teamreport_sorted) {
print "name:\t$_->{'name'}\tquantity:\t$_->{'quantity'}\n";
}

__END__

name: n0 quantity: 50
name: n1 quantity: 48
name: n2 quantity: 46
name: n3 quantity: 44
======================
name: n3 quantity: 44
name: n2 quantity: 46
name: n1 quantity: 48
name: n0 quantity: 50

I would imagine this is probably a better way
 
R

robic0

Here is part of it, I think that this is enough to be relevent

I don't mean to be rude, but Paul clearly asked for something that is
short (about 20 lines is reasonably considered short), and most
importantly, cut-and-pasteable.

Here's what something like that might look like:

#!/usr/bin/perl
use warnings;
use strict;

my @unsorted = ( { id => 03, name => "hi", count=> 200 },
{ id => 01, name => "hi", count=> 100 },
{ id => 02, name => "hi", count=> 150 } );

my @sorted = sort { $a->{count} <=> $b->{count} } @unsorted;

print "id: [$_->{id}] name: [$_->{name}], count: [$_->{count}]\n" for @sorted;
__END__

This gives me:

id: [1] name: [hi], count: [100]
id: [2] name: [hi], count: [150]
id: [3] name: [hi], count: [200]
Yes yours work Eric because you use anonymous hash array reference.
However, if you glanced at the OP's code you would have noticed
he used a reference to a hash array, which was probobaly his
extreme desire to make this work after hours of head bashing.

Maybe you should pause to consider posters desire to make his/her
code work before you go into acedemia attack mode on style, context
and construct...

his:
%matrix = ('key' => '');
push(@teamreport ,\%matrix);
${$teamreport[0]}{key} = 'value';

is not the same as

yours:
$matrix = {'key' => ''};
push(@teamreport ,$matrix);
$teamreport[0]->{key} = 'value';
 
M

Matt Garrish

use strict;
use warnings;
use sort 'stable';

my @teamreport = ();
for (my $i = 0; $i < 4; $i++) {
my $matrix = {};
$matrix->{'name'} = "n$i";
$matrix->{'date'} = "d$i";
$matrix->{'quantity'} = 50-(2*$i);
$matrix->{'nothing'} = 'nothing';
push(@teamreport ,$matrix);
}
my @teamreport_sorted = sort { $a->{'quantity'} <=> $b->{'quantity'}
} @teamreport;

for (@teamreport) {
print "name:\t$_->{'name'}\tquantity:\t$_->{'quantity'}\n";
}

print "======================\n";

[pet peeve]

print '='x22, "\n";

Matt
 
E

Eric Schwartz

Yes yours work Eric because you use anonymous hash array reference.
However, if you glanced at the OP's code you would have noticed
he used a reference to a hash array, which was probobaly his
extreme desire to make this work after hours of head bashing.

Maybe you should pause to consider posters desire to make his/her
code work before you go into acedemia attack mode on style, context
and construct...

Maybe if the poster had actually posted a short, executable example,
I'd have been able to more easily read it, which was the point in the
first place. My critique was hardly "academia" (sic); it was to the
point. I think it will help the OP far more to learn how to ask for
help in the best way possible, than to spend 3 times as long to try to
understand his example.

And for your reference, the OP himself admitted that his example was
difficult to follow, and thanked me for my advice. Take that for what
you will.

-=Eric
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top