sorting an array with associated values in separate arrays

B

Ben

Hi,

I have the following arrays, each with the same number of elements.
Each element of @main is associated with each corresponding element of
@b, @c, and @d. i.e., the $main[1] is associated with $b[1],
$x[1], ..., $z[1].

@main = ( 1, 3, 2, 12, 5, 7, 2, 9); # these are likely to be non-
unique floating point values in my real application
@b = ( 1, 1, 2, 1, 1, 3, 1, 3); # the rest of these are
integer values
@x = ( 0, 9, 9, 9, 8, 8, 8, 1);
@d = ( 1, 1, 1, 1, 1, 1, 1, 1);
....

I would like to sort @main numerically in ascending order, and have
@b, @x, @d, ... rearranged according to the new sorted order while
maintaining the original association. Seems like a perfect job for
a hash, I'm just not that comfortable with how to set it up in this
case.
I think that I should create a hash of array references, but I don't
know how to sort the elements (values) of "@main" and subsequently
update the other arrays with the new order. A more brute force
option that occurrs to me is to simply sort the array @main, find the
new "positions" of the sorted array relative to the original and
reorder @b, @x, @d, ... according to the new positions, without using
a hash, but using some loops. Is there a better approach here?
Note: It may be a "completely obvious" different way, I'm a perl
novice, so it is quite probable to find an easy alternate approach.

Additional information:
To populate the hash named "tmp", I'm using:
%tmp = ( );
foreach $x (@main) {
push( @{$tmp{main}}, $x );
push( @{$tmp{b}}, $b[$i] );
push( @{$tmp{x}}, $x[$i] );
push( @{$tmp{d}}, $d[$i] );
...
$i++;
}

I know how to sort a key-value hash by value:
@sorted = sort { $hash{$a} <=> $hash{$b} } keys %hash;

Just not making the next perlogical step in how to do this in the way
described above.

Aside: For populating the hash, I also tried to use the module
Tie::Hash::MultiValue, but it complains that:
Can't locate object method "TIEHASH" via package
"Tie::Hash::Multivalue" at x.pl line 68. No idea what i'm doing
wrong. I installed Tie::Hash::Multivalue following the instructions
contained in the module's README file, no error messages or warnings
running perl -w. I'm not using strict.

Thanks for your advice.
-Ben
 
B

Ben

Hi,

I have the following arrays, each with the same number of elements.
Each element of @main is associated with each corresponding element of
@b, @c, and @d. i.e., the $main[1] is associated with $b[1],
$x[1], ..., $z[1].

I believe I figured it out:

Create individual hashes: %mainhash, %bhash, %xhash, ..., sort
mainhash by value, and keep the sorted keys for use in %bhash, etc..
I didn't initially think that the "keys" would be necessarily the same
for different hashes (although I had no good reason to think this).

#populate hashes
for $i (0..$#main) {
$mainhash{$i} = $main[$i];
$bhash{$i} = $b[$i];
$xhash{$i} = $x[$i];
$dhash{$i} = $d[$i];
...
}

#sort mainhash by value
@sorted = sort { $mainhash{$a} <=> $mainhash{$b} } keys %mainhash;

# print sorted hashes
foreach $key (@sorted) {
print "a: $key => $mainhash{$key}\n";
print "b: $key => $bhash{$key}\n";
print "x: $key => $xhash{$key}\n";
print "d: $key => $dhash{$key}\n";
...
}

Additional comments welcome.

-Ben
 
B

Ben

Hi,

I have the following arrays, each with the same number of elements.
Each element of @main is associated with each corresponding element of
@b, @c, and @d. i.e., the $main[1] is associated with $b[1],
$x[1], ..., $z[1].

I believe I figured it out:

Create individual hashes: %mainhash, %bhash, %xhash, ..., sort
mainhash by value, and keep the sorted keys for use in %bhash, etc..
I didn't initially think that the "keys" would be necessarily the same
for different hashes (although I had no good reason to think this).

#populate hashes
for $i (0..$#main) {
$mainhash{$i} = $main[$i];
$bhash{$i} = $b[$i];
$xhash{$i} = $x[$i];
$dhash{$i} = $d[$i];
...
}

#sort mainhash by value
@sorted = sort { $mainhash{$a} <=> $mainhash{$b} } keys %mainhash;

# print sorted hashes
foreach $key (@sorted) {
print "a: $key => $mainhash{$key}\n";
print "b: $key => $bhash{$key}\n";
print "x: $key => $xhash{$key}\n";
print "d: $key => $dhash{$key}\n";
...
}

Additional comments welcome. Hey, I can go to sleep now. :)

-Ben
 
M

Mumia W.

Hi,

I have the following arrays, each with the same number of elements.
Each element of @main is associated with each corresponding element of
@b, @c, and @d. i.e., the $main[1] is associated with $b[1],
$x[1], ..., $z[1].

I believe I figured it out:

Create individual hashes: %mainhash, %bhash, %xhash, ..., sort
mainhash by value, and keep the sorted keys for use in %bhash, etc.. [...]

I'm glad you figured it out. Another, possibly simpler way would be to
compact the separate (parallel) arrays into a single, two-dimensional
array and sort that:

use strict;
use warnings;

my (@main, @b, @c, @d);
@main = ( 1, 3, 2, 12, 5, 7, 2, 9);
@b = ( 1, 1, 2, 1, 1, 3, 1, 3);
@c = ( 0, 9, 7, 9, 8, 2, 4, 1);
@d = ( 1, 2, 5, 7, 8, 1, 5, 6);

my @compact;
foreach my $pos (0..$#main) {
push @compact, [$main[$pos], $b[$pos], $c[$pos], $d[$pos]];
}

print "@$_\n" for (@compact);
@compact = sort { $a->[0] <=> $b->[0] } @compact;
print "--------------------\n";
print "@$_\n" for (@compact);

The data is printed transposed, but it is sorted properly.

You might need to read about references:
Start->Run->"perldoc perlreftut"
 
A

anno4000

Ben said:
Hi,

I have the following arrays, each with the same number of elements.
Each element of @main is associated with each corresponding element of
@b, @c, and @d. i.e., the $main[1] is associated with $b[1],
$x[1], ..., $z[1].

@main = ( 1, 3, 2, 12, 5, 7, 2, 9); # these are likely to be non-
unique floating point values in my real application
@b = ( 1, 1, 2, 1, 1, 3, 1, 3); # the rest of these are
integer values
@x = ( 0, 9, 9, 9, 8, 8, 8, 1);
@d = ( 1, 1, 1, 1, 1, 1, 1, 1);
...

I would like to sort @main numerically in ascending order, and have
@b, @x, @d, ... rearranged according to the new sorted order while
maintaining the original association. Seems like a perfect job for
a hash, I'm just not that comfortable with how to set it up in this
case.

That's indirect sorting, and it's done with arrays, not hashes.

You first build an array of indices (@i below) that contains the
indices to @main in the desired order. Now Perl's array slices
come in handy to reconstruct @main in sorted order and the other
arrays in the corresponding order. Here is how:

my @main = ( 1, 3, 2, 12, 5, 7, 2, 9);
my @b = ( 1, 1, 2, 1, 1, 3, 1, 3);
my @x = ( 0, 9, 9, 9, 8, 8, 8, 1);
my @d = ( 1, 1, 1, 1, 1, 1, 1, 1);

# build the index array
my @i = sort { $main[ $a] <=> $main[ $b] } 0 .. $#main;

# now @main[ @i] is the ordered array, and @b[ @i] is the
# corresponding order of @b, etc.

# print the re-ordered arrays
my $fmt = join( ' ', ( '%2.0f') x @main) . "\n";
printf $fmt, @$_[ @i] for \ ( @main, @b, @x, @d);

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,754
Messages
2,569,527
Members
44,998
Latest member
MarissaEub

Latest Threads

Top