Sorting a Hash of Arrays by an Element in the Array

R

robic0

I thought it was: you can not retrieve the keys of a hash in the order
they were inserted due to the way they are stored.

Len

for ($i = 0; $ i < 5; $i++) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}
 
A

axel

l v said:
Alf McLaughlin wrote:
I thought it was: you can not retrieve the keys of a hash in the order
they were inserted due to the way they are stored.

In fact in the more recent editions of perl, and I am sure someone
will correct me if I am wrong, it has been deliberately made
more difficult.

Axel
 
U

usenet

In fact in the more recent editions of perl, and I am sure someone
will correct me if I am wrong, it has been deliberately made
more difficult [to retrieve hash keys in order of insertion].

In Perl 5.8.1, the algorithm to seed a hash was changed to make hash
key distribution even more random. But it wasn't intended to throw up
obstacles to the programmer; it is a security measure.

See perlsec
(http://perldoc.perl.org/perlsec.html#Algorithmic-Complexity-Attacks)
 
L

l v

robic0 said:
I thought it was: you can not retrieve the keys of a hash in the order
they were inserted due to the way they are stored.

Len

for ($i = 0; $ i < 5; $i++) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}

um. No. You've sorted a hash which you loaded in sorted order.

for $i (5, 2, 4, 1) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}

which prints:
1
2
4
5

clearly *not* the _order_ in which the hash was loaded.

Len
 
J

John Bokma

l v said:
robic0 said:
Alf McLaughlin wrote:

noticed that some people like to answer this question as "this cannot
be done because you cannot guarantee keys in a hash are stored in a
sorted manner... etc etc".


Thanks,
Alf

I thought it was: you can not retrieve the keys of a hash in the order
they were inserted due to the way they are stored.

Len

for ($i = 0; $ i < 5; $i++) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}

um. No. You've sorted a hash which you loaded in sorted order.

for $i (5, 2, 4, 1) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}

which prints:
1
2
4
5

clearly *not* the _order_ in which the hash was loaded.

The counter proof shows that you *can* retrieve keys of hash in the
order they were inserted, you insert then sorted, and retrieve them
sorted ;-)
 
R

robic0

robic0 said:
Alf McLaughlin wrote:

noticed that some people like to answer this question as "this cannot
be done because you cannot guarantee keys in a hash are stored in a
sorted manner... etc etc".


Thanks,
Alf

I thought it was: you can not retrieve the keys of a hash in the order
they were inserted due to the way they are stored.

Len

for ($i = 0; $ i < 5; $i++) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}

um. No. You've sorted a hash which you loaded in sorted order.

for $i (5, 2, 4, 1) {
$hash{$i} = [$i];
}
for my $key (sort {$hash{$a}->[0] <=> $hash{$b}->[0]} keys %hash) {
print "$key\n";
}
$cnt = 0;
for $i (5, 2, 4, 1) {
$hash{$i} = [$i,$cnt++];
}
for my $key (sort {$hash{$a}->[1] <=> $hash{$b}->[1]} keys %hash) {
print "$key\n";
}
 
A

Alf McLaughlin

I almost feel like you could write a whole book about sorting hashes!

Ok, so I dig that you can't sort things deep inside of hashes of hashes
and get anything meaningful to the outer keys. However, let's say the
way I designed things I have pairs of keys in a hash of a hash that
then have a score (among other elements) embedded in an array. This is
basically my 2nd question within this post. So, if that didn't make
sense I've got this:

$hash{$key1}{$key2} = [($score1, $score2, $score3)];

something like that, dig it? Now, I want to sort this bitch by score
#2. BUT, I'd really like to sort it on the fly using only 1 sort
command (because I'm super lazy and I'd rather write one complex thing
than a bunch of smaller, simple things). Can I do this the way I
sorted one array element in my hash? This was for a hash like this (as
discussed over and over again in this thread... nothing new here):

$hash{$key1} = [($score1, $score2, $score3)];

for my $key1 (sort {$hash{$a}->[1] <=> $hash{$b}->[1]} keys %hash) {
#keys are sorted by score 2.
}

That was the old example. NOW, I envision something that looks like
this (but I know this isn't right at all):

for my $key1 (keys %hash) {
for my $key2 (sort {$hash{$key1}{$a}->[1] <=>
{$hash{$key2}{$b}->[1]} keys %{$hash{$key1}}) {
# I WISH the keys were sorted by score 2, but this doesn't work.
:-(
}

Maybe this is just supremely retarded of me, but I'd still love to see
it work! I will gladly attempt to clarify if this isn't understood
because I do think this question is somewhat hard to convey (whereas
the last one was pretty transparent, I think). Anybody have a simple
way to sort this thing the way I want to do it?
 
X

xhoster

Alf McLaughlin said:
I almost feel like you could write a whole book about sorting hashes!

Ok, so I dig that you can't sort things deep inside of hashes of hashes
and get anything meaningful to the outer keys. However, let's say the
way I designed things I have pairs of keys in a hash of a hash that
then have a score (among other elements) embedded in an array. This is
basically my 2nd question within this post. So, if that didn't make
sense I've got this:

$hash{$key1}{$key2} = [($score1, $score2, $score3)];

something like that, dig it? Now, I want to sort this bitch by score
#2. BUT, I'd really like to sort it on the fly using only 1 sort
command (because I'm super lazy and I'd rather write one complex thing
than a bunch of smaller, simple things). Can I do this the way I
sorted one array element in my hash? This was for a hash like this (as
discussed over and over again in this thread... nothing new here):

$hash{$key1} = [($score1, $score2, $score3)];

for my $key1 (sort {$hash{$a}->[1] <=> $hash{$b}->[1]} keys %hash) {
#keys are sorted by score 2.
}

Something like:


for my $key_pair ( sort {$hash{$a->[0]}{$a->[1]}[1] <=>
$hash{$b->[0]}{$b->[1]}[1] }
map {my $t=$_; map [$t,$_], keys %{$hash{$t}}} keys
%hash) {
# $key_pair is sorted by score 2
}


Xho
 
J

Jürgen Exner

Alf said:
I almost feel like you could write a whole book about sorting hashes!

Ok, so I dig that you can't sort things deep inside of hashes of
hashes and get anything meaningful to the outer keys.

Nonsense. Of course you can.
For example if you have a Hash of (references to) Arrays. Then nothing
prevents you from sorting those arrays.

jue
 
A

Alf McLaughlin

Ok, I agree. I think that original statement was confusing. What I
was trying to say is that if you decide to sort by the array element
that original key will not necessarily be sorted (this is really
obvious since you aren't actually sorting by the key and is just a
stupid thing to make a point out of, actually).
 
R

robic0

Ok, I agree. I think that original statement was confusing. What I
was trying to say is that if you decide to sort by the array element
that original key will not necessarily be sorted (this is really
obvious since you aren't actually sorting by the key and is just a
stupid thing to make a point out of, actually).

You have to STOP thinking in terms of the whole thing when working
with nested arrays. The container, the "outer" array, holds the
references to the first level, thats it! The reason thats so
important is that the outer (container) level gets you "access"
to the inner level data that was the OBJECT of your sort.
After a several inner layer sort, the container contains the
"access" order of the inner data. All you have to do is sequentially
use the outer references to print the inner dataa that was the
OBJECT of the sort. Everytime you sort inner data, what you are
changing is the container, NOT the inner data containers.
The container is a single dimentioned array, a 1x10 or 1x20
of references, and is the result of the sort. NO data has
changed anywhere else.
Hope this helps.....

And your driveing youself nuts with notation like:
$v{$ttt}{$xxx}, don't use that notation!
 
A

Anno Siegel

Alf McLaughlin said:
I almost feel like you could write a whole book about sorting hashes!

Ok, so I dig that you can't sort things deep inside of hashes of hashes
and get anything meaningful to the outer keys. However, let's say the
way I designed things I have pairs of keys in a hash of a hash that
then have a score (among other elements) embedded in an array. This is
basically my 2nd question within this post. So, if that didn't make
sense I've got this:

$hash{$key1}{$key2} = [($score1, $score2, $score3)];

something like that, dig it? Now, I want to sort this bitch by score
#2. BUT, I'd really like to sort it on the fly using only 1 sort
command (because I'm super lazy and I'd rather write one complex thing
than a bunch of smaller, simple things). Can I do this the way I
sorted one array element in my hash? This was for a hash like this (as
discussed over and over again in this thread... nothing new here):

$hash{$key1} = [($score1, $score2, $score3)];

for my $key1 (sort {$hash{$a}->[1] <=> $hash{$b}->[1]} keys %hash) {
#keys are sorted by score 2.
}

Something like:


for my $key_pair ( sort {$hash{$a->[0]}{$a->[1]}[1] <=>
$hash{$b->[0]}{$b->[1]}[1] }
map {my $t=$_; map [$t,$_], keys %{$hash{$t}}} keys
%hash) {
# $key_pair is sorted by score 2
}

In a variant, one could first built a list of triplets which contain
the key pair along with the score value from the second level hash.
This is a kind of Schwartz Transform. It simplifies the sort operation
at the cost of first inserting, then removing the sort key:

my @sorted_pairs =
map [ @$_[ 0, 1]],
sort { $a->[ 2] <=> $b->[ 2] }
map {
my $t = $_;
map [ $t, $_, $hash{ $t}->{ $_}->[ 1]], keys %{ $hash{ $t}}
} keys %hash;

Anno
 
A

Anno Siegel

Anno Siegel said:
In a variant, one could first built a list of triplets which contain
the key pair along with the score value from the second level hash.
This is a kind of Schwartz Transform. It simplifies the sort operation
at the cost of first inserting, then removing the sort key:

my @sorted_pairs =
map [ @$_[ 0, 1]],
sort { $a->[ 2] <=> $b->[ 2] }
map {
my $t = $_;
map [ $t, $_, $hash{ $t}->{ $_}->[ 1]], keys %{ $hash{ $t}}
} keys %hash;

Oh... and here's a variation that uses pseudo-multidimensional hash
syntax. It delivers key pairs as blank-separated strings (assuming
no blanks appear in keys):

my @sorted_pairs = do {
my %aux;
local $; = ' ';
for my $t ( keys %hash ) {
$aux{ $t, $_} = $hash{ $t}->{ $_}->[ 1] for keys %{ $hash{ $t}};
}
sort { $aux{ $a} <=> $aux{ $b} } keys %aux;
};

Ah... the temptations of doing it in yet another way... I'll stop now.

Anno
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top