Nested sorting of a hash

J

Justin C

I have a hash containing stock codes (the keys) and the number of units
sold (the values). I can sort them easily to give the best selling items
at the top and those that sold the fewest at the bottom. There are many
items with the same number of units sold, what I'd like to do is sort
the keys within there relevant position in the in the list.

Here is an example of the data I have:

Code Qty
ABZ001 13
ADF001 7
ADF002 6
ADF003 6
ADF004 6
ABZ002 5
ABZ003 5
ABZ120 4
ABZ047 4
ABZ022 4
ADF027 4
ADF019 4

What I'd like to have is within those items that have sold 4 units they
be sorted asciibetically.

The sort I have for the hash so far is:

foreach ( sort { $sales{$b} <=> $sales{$a} } keys %sales) {
print "$_;\t$sales{$_}\n";
}

Thank you for any help you can give with this.

Justin.
 
B

Ben Morrow

Quoth (e-mail address removed):
I have a hash containing stock codes (the keys) and the number of units
sold (the values). I can sort them easily to give the best selling items
at the top and those that sold the fewest at the bottom. There are many
items with the same number of units sold, what I'd like to do is sort
the keys within there relevant position in the in the list.
The sort I have for the hash so far is:

foreach ( sort { $sales{$b} <=> $sales{$a} } keys %sales) {

foreach ( sort {
$sales{$b} <=> $sales{$a}
||
$a cmp $b
} keys %sales ) {
print "$_;\t$sales{$_}\n";
}

The only time a sort expression returns false is when they compared
equal, so you can combine them with ||.

Ben
 
T

Tad McClellan

Justin C said:
I have a hash containing stock codes (the keys) and the number of units
sold (the values).
Here is an example of the data I have:

Code Qty
ABZ001 13
ADF001 7
ADF002 6
ADF003 6
ADF004 6
ABZ002 5
ABZ003 5
ABZ120 4
ABZ047 4
ABZ022 4
ADF027 4
ADF019 4

What I'd like to have is within those items that have sold 4 units they
be sorted asciibetically.


Your question is answered in the Perl FAQ:

How do I sort a hash (optionally by value instead of key)?

The sort I have for the hash so far is:

foreach ( sort { $sales{$b} <=> $sales{$a} } keys %sales) {
print "$_;\t$sales{$_}\n";
}


foreach ( sort { $sales{$b} <=> $sales{$a} or $a cmp $b } keys %sales) {
 
J

Justin C

Quoth (e-mail address removed):


foreach ( sort {
$sales{$b} <=> $sales{$a}
||
$a cmp $b
} keys %sales ) {


The only time a sort expression returns false is when they compared
equal, so you can combine them with ||.

Thank you for that. It's quite elegant really. Thanks also to Glenn for
(almost) the same answer... I say almost, is there any difference
between || and 'or' in the above?

Justin.
 
J

Jürgen Exner

bugbear said:
You need to make your hash into an array of hashes, and sort that.

No, you don't.
You cannot get the results you desire by sorting the keys;
you must sort by considering the code/sold as a pair,
using a primary/secondary comparison in the sort.

Which is trivial to do by augmenting the inline compare function with a
secondary comparison in case the first one yields equal:

$sales{$b} <=> $sales{$a} or $a cmp $b

This neat trick is mentioned in the docs, BTW:

If you need to sort on several fields, the following paradigm is
useful.
@sorted = sort { field1($a) <=> field1($b) ||
field2($a) cmp field2($b) ||
field3($a) cmp field3($b)
} @data;

jue
 
J

Jürgen Exner

Glenn said:
If the field lookup is expensive,

Which obviously is not the case for the OP.
you'd want the orcish maneuver or a
schwartzian transform:
http://www.perlmonks.org/?node_id=128722

Which is actually mentioned in the lines above the except that I quoted with
an additional pointer just following my excerpt (see 'perldoc -q sort'):

This can be conveniently combined with precalculation of keys as
given above.

I don't think there is any need to emphasize the point in the FAQ even
further.

jue
 
U

Uri Guttman

GJ> If the field lookup is expensive, you'd want the orcish maneuver or a
GJ> schwartzian transform:
GJ> http://www.perlmonks.org/?node_id=128722

the orcish is actually fairly slow. but you can compare all the sort
speedup techniques with Sort::Maker and even see the generated code. the
benchmark script in the tarball shows that the GRT is the fastest for
most multilevel sorting.

uri
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top