Home > Archive > PERL Miscellaneous > February 2007 > sorting an array with associated values in separate arrays
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
sorting an array with associated values in separate arrays
|
|
|
| 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
| |
|
| On Feb 23, 3:23 am, "Ben" <jben...@gmail.com> wrote:
> 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
| |
|
| On Feb 23, 3:23 am, "Ben" <jben...@gmail.com> wrote:
> 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
| |
| Mumia W. 2007-02-23, 8:04 am |
| On 02/23/2007 03:11 AM, Ben wrote:
> On Feb 23, 3:23 am, "Ben" <jben...@gmail.com> wrote:
>
> 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"
--
Windows Vista and your freedom in conflict:
http://techdirt.com/articles/20061019/102225.shtml
| |
| Michele Dondi 2007-02-23, 8:04 am |
| On 23 Feb 2007 00:23:04 -0800, "Ben" <jbenjam@gmail.com> wrote:
>I have the following arrays, each with the same number of elements.
Then it's probably a bad idea. A AoA or HoA may be a better one.
Michele
--
{$_=pack'B8'x25,unpack'A8'x32,$a^=sub{po
p^pop}->(map substr
(($a||=join'',map--$|x$_,(unpack'w',unpack'u','G^<R<Y]*YB='
..'KYU;*EVH[.FHF2W+#"\Z*5TI/ER<Z`S(G.DZZ9OX0Z')=~/./g)x2,$_,
256),7,249);s/[^\w,]/ /g;$ \=/^J/?$/:"\r";print,redo}#JAPH,
| |
| anno4000@radom.zrz.tu-berlin.de 2007-02-23, 7:07 pm |
| Ben <jbenjam@gmail.com> wrote in comp.lang.perl.misc:
> 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
| |
| Lars Haugseth 2007-02-26, 8:04 am |
|
* "Ben" <jbenjam@gmail.com> wrote:
>
> 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.
The approach I'd use is to store the data as an array of hashes:
my @data = ({ main => 1, b => 1, x => 0, d => 1, ...},
{ main => 3, b => 1, x => 9, d => 1, ...},
{ main => 2, b => 2, x => 9, d => 1, ...},
... );
You can then get a sorted version by simply doing
my @data_sorted = sort { $a->{main} <=> $b->{main} } @data;
--
Lars Haugseth
"If anyone disagrees with anything I say, I am quite prepared not only to
retract it, but also to deny under oath that I ever said it." -Tom Lehrer
|
|
|
|
|