Sorting AofH over hash key(s)...

U

/usr/ceo

I've searched this NG, the web, and CPAN and haven't seen anything to
do this, and while I have a solution, I'm wondering what other
solutions might be living inside some other pretty brilliant brain
cages here on c.l.p.m...

The problem seems fairly straight-forward: I want to be able to sort
an Array of Hashes by an arbitrary number of hash keys. For instance,
given this hash:

my $h = [
{ color => 'red', size => 1, width => 640, height => 480 },
{ color => 'blue', size => 4, width => 800, height => 600 },
{ color => 'green', size => 2, width => 1024, height => 768 },
{ color => 'orange', size => 5, width => 320, height => 280 },
{ color => 'purple', size => 3, width => 40, height => 50 },
];

I'd like to sort by the keys: color, size, width, and/or height. I
realize for only ONE hash key, I can do:

for my $info (sort { $a->{color} cmp $b->{color} } @{$h}) { blah() }
#...

But... What if I want to sort by say... color and then size? Or
height and then width? Or all the keys in some order? Again haven't
seen anything to do this. It's hard to imagine this has never been
solved, but then again, I've done a fair amount of Perl coding in my
day and I haven't run across the need to do this until recently.

My solution? Somewhat similar to a mod I saw on CPAN once that
flattened deep hashes into a dotted namespace. It was a serialization
(or a namespace transformation, more like it):

$h->{norman}->{width}-{arm}->{finger}->{pinky} = 1.5;

became:

$h{norman.width.arm.finger.pinky} = 1.5;

Or something like that. Anyway, my approach was to serialize the data
(not the keys) in the hashes in order of keys I wanted to sort on,
sort THAT array, and deserialize back in the right key order:

my $temp = [
'red\xFF1\xFF640\xFF480',
'blue\xFF4\xFF800\xFF600',
'green'\xFF2\xFF1024\xFF768',
'orange\xFF5\xFF320\xFF280',
'purple\xFF\xFF3\xFF40\xFF50',
];

my @sortedArray = sort @{$temp};

## Then deserialize in correct order...

Also, my example, with a lot of numeric data is somewhat bad, I
realize. I realize my solution doesn't sort numeric data properly
(due to ordinal values), but for the solution I needed it for,
immediately, the serialize -> sort -> deserialize method works for
now. The numeric/ordinal issue is one of the reasons I am asking
simply from a purist standpoint.

Anyone got something different/better? A mod to recommend that I am
not seeing on CPAN?

Thanks!
/usr/ceo
 
M

Mirco Wahab

/usr/ceo said:
my $h = [
{ color => 'red', size => 1, width => 640, height => 480 },
{ color => 'blue', size => 4, width => 800, height => 600 },
{ color => 'green', size => 2, width => 1024, height => 768 },
{ color => 'orange', size => 5, width => 320, height => 280 },
{ color => 'purple', size => 3, width => 40, height => 50 },
];

I'd like to sort by the keys: color, size, width, and/or height. I
realize for only ONE hash key, I can do:

for my $info (sort { $a->{color} cmp $b->{color} } @{$h}) { blah() }
#...

But... What if I want to sort by say... color and then size? Or
height and then width? Or all the keys in some order? Again haven't
seen anything to do this. It's hard to imagine this has never been
solved, but then again, I've done a fair amount of Perl coding in my
day and I haven't run across the need to do this until recently.

The (somehow) canonical solution would be a sorter function like:

...
my @hs = sort mysortorder @$h;

sub mysortorder {
$a->{color} cmp $b->{color} ||
$b->{width} <=> $a->{width} ||
$b->{height} <=> $a->{height} ||
$b->{size} <=> $a->{size}
}

for my $s (@hs) {
print "$_=>$s->{$_}, " for keys %$s;
print "\n"
}
...

Regards

M.
 
N

nolo contendere

I've searched this NG, the web, and CPAN and haven't seen anything to
do this, and while I have a solution, I'm wondering what other
solutions might be living inside some other pretty brilliant brain
cages here on c.l.p.m...

The problem seems fairly straight-forward: I want to be able to sort
an Array of Hashes by an arbitrary number of hash keys. For instance,
given this hash:

my $h = [
{ color => 'red', size => 1, width => 640, height => 480 },
{ color => 'blue', size => 4, width => 800, height => 600 },
{ color => 'green', size => 2, width => 1024, height => 768 },
{ color => 'orange', size => 5, width => 320, height => 280 },
{ color => 'purple', size => 3, width => 40, height => 50 },
];

I'd like to sort by the keys: color, size, width, and/or height. I
realize for only ONE hash key, I can do:

for my $info (sort { $a->{color} cmp $b->{color} } @{$h}) { blah() }
#...

But... What if I want to sort by say... color and then size? Or
height and then width? Or all the keys in some order? Again haven't
seen anything to do this. It's hard to imagine this has never been
solved, but then again, I've done a fair amount of Perl coding in my
day and I haven't run across the need to do this until recently.

My solution? Somewhat similar to a mod I saw on CPAN once that
flattened deep hashes into a dotted namespace. It was a serialization
(or a namespace transformation, more like it):

$h->{norman}->{width}-{arm}->{finger}->{pinky} = 1.5;

became:

$h{norman.width.arm.finger.pinky} = 1.5;

Or something like that. Anyway, my approach was to serialize the data
(not the keys) in the hashes in order of keys I wanted to sort on,
sort THAT array, and deserialize back in the right key order:

my $temp = [
'red\xFF1\xFF640\xFF480',
'blue\xFF4\xFF800\xFF600',
'green'\xFF2\xFF1024\xFF768',
'orange\xFF5\xFF320\xFF280',
'purple\xFF\xFF3\xFF40\xFF50',
];

my @sortedArray = sort @{$temp};

## Then deserialize in correct order...

Also, my example, with a lot of numeric data is somewhat bad, I
realize. I realize my solution doesn't sort numeric data properly
(due to ordinal values), but for the solution I needed it for,
immediately, the serialize -> sort -> deserialize method works for
now. The numeric/ordinal issue is one of the reasons I am asking
simply from a purist standpoint.

# from the Perl Cookbook

my @sorted =
sort { $a->name cmp $b->name
||
$b->age <=> $a->age } @employees;
 
J

J. Gleixner

/usr/ceo said:
I've searched this NG, the web, and CPAN and haven't seen anything to
do this, and while I have a solution, I'm wondering what other
solutions might be living inside some other pretty brilliant brain
cages here on c.l.p.m...

The problem seems fairly straight-forward: I want to be able to sort
an Array of Hashes by an arbitrary number of hash keys. For instance,
given this hash:

my $h = [
{ color => 'red', size => 1, width => 640, height => 480 },
{ color => 'blue', size => 4, width => 800, height => 600 },
{ color => 'green', size => 2, width => 1024, height => 768 },
{ color => 'orange', size => 5, width => 320, height => 280 },
{ color => 'purple', size => 3, width => 40, height => 50 },
];

I'd like to sort by the keys: color, size, width, and/or height. I
realize for only ONE hash key, I can do:

for my $info (sort { $a->{color} cmp $b->{color} } @{$h}) { blah() }
#...

But... What if I want to sort by say... color and then size? Or
height and then width? Or all the keys in some order?

Just modify the sort to take into account the other keys.

sort {
$a->{ 'color' } cmp $b->{ 'color' } ||
$a->{ 'size' } <=> $b->{ 'size' }
}@{$h};

perldoc -q "How do I sort"
 
U

/usr/ceo

[Problem description snipped]

# from the Perl Cookbook

my @sorted =
sort { $a->name cmp $b->name
||
$b->age <=> $a->age } @employees;

Dad-gummit, I *looked* in the PC, and didn't see this... That syntax
makes perfect sense. It never occurred to me to extended the { sort
$a->{key} cmp $b->{key} } syntax into something broader with logical
operators. You get stuck in a rut with what you see as 90% used
sometimes.

NOW the solution is on the internet... :)

Thanks!
/usr/ceo
 
T

Tad McClellan

/usr/ceo said:
[Problem description snipped]

# from the Perl Cookbook

my @sorted =
sort { $a->name cmp $b->name
||
$b->age <=> $a->age } @employees;

Dad-gummit, I *looked* in the PC, and didn't see this... That syntax
makes perfect sense. It never occurred to me to extended the { sort
$a->{key} cmp $b->{key} } syntax into something broader with logical
operators. You get stuck in a rut with what you see as 90% used
sometimes.

NOW the solution is on the internet... :)


Errr, it was already on the internet...

http://faq.perl.org/perlfaq4.html#How_do_I_sort_a_hash

.... and on your very own hard disk!

perldoc -q sort
 
S

Salvador Fandino

/usr/ceo said:
I've searched this NG, the web, and CPAN and haven't seen anything to
do this, and while I have a solution, I'm wondering what other
solutions might be living inside some other pretty brilliant brain
cages here on c.l.p.m...

The problem seems fairly straight-forward: I want to be able to sort
an Array of Hashes by an arbitrary number of hash keys. For instance,
given this hash:
....

Anyone got something different/better? A mod to recommend that I am
not seeing on CPAN?

You can use Sort::Key or Sort::Maker from CPAN to do that:

For instance, to sort by color then by width:

use Sort::Key::Multi qw(si_keysort);
# si_ stands for string + integer
# is like saying use 'cmp' to compare the first key
# and '<=>' for the second

my @sorted = si_keysort { $_->{color}, $_->{width} } @$h;


You can even generate the sorter function dynamically:

use Sort::Key qw(multikeysorter);

# define how the different data properties have to be compared:
my %sorting_types = (color => 'string',
size => 'integer',
width => 'integer',
height => 'integer');

my @order = qw(color size width); # change to suit your needs;

my @sorting_types = map $sorting_types{$_}, @order;

my $sorter = multikeysorter(sub {
@{$_}{@order} # that's a hash slice...
},
@sorting_types);

my $sorted_data = $sorter->(@data);

Cheers,

- Salva
 
S

Salvador Fandino

/usr/ceo said:
I've searched this NG, the web, and CPAN and haven't seen anything to
do this, and while I have a solution, I'm wondering what other
solutions might be living inside some other pretty brilliant brain
cages here on c.l.p.m...

The problem seems fairly straight-forward: I want to be able to sort
an Array of Hashes by an arbitrary number of hash keys. For instance,
given this hash:
....

Anyone got something different/better? A mod to recommend that I am
not seeing on CPAN?

You can use Sort::Key or Sort::Maker from CPAN to do that:

For instance, to sort by color then by width:

use Sort::Key::Multi qw(si_keysort);
# si_ stands for string + integer
# is like saying use 'cmp' to compare the first key
# and '<=>' for the second

my @sorted = si_keysort { $_->{color}, $_->{width} } @$h;


You can even generate the sorter function dynamically:

use Sort::Key qw(multikeysorter);

# define how the different data properties have to be compared:
my %sorting_types = (color => 'string',
size => 'integer',
width => 'integer',
height => 'integer');

my @order = qw(color size width); # change to suit your needs;

my @sorting_types = map $sorting_types{$_}, @order;

my $sorter = multikeysorter(sub {
@{$_}{@order} # that's a hash slice...
},
@sorting_types);

my $sorted_data = $sorter->(@data);

Cheers,

- Salva
 

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,992
Messages
2,570,220
Members
46,805
Latest member
ClydeHeld1

Latest Threads

Top