sorting an array with associated values in separate arrays

Discussion in 'Perl Misc' started by Ben, Feb 23, 2007.

  1. Ben

    Ben Guest

    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
    Ben, Feb 23, 2007
    #1
    1. Advertising

  2. Ben

    Ben Guest

    On Feb 23, 3:23 am, "Ben" <> 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
    Ben, Feb 23, 2007
    #2
    1. Advertising

  3. Ben

    Ben Guest

    On Feb 23, 3:23 am, "Ben" <> 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
    Ben, Feb 23, 2007
    #3
  4. Ben

    Mumia W. Guest

    On 02/23/2007 03:11 AM, Ben wrote:
    > On Feb 23, 3:23 am, "Ben" <> 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'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
    Mumia W., Feb 23, 2007
    #4
  5. Ben

    -berlin.de Guest

    Ben <> 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
    -berlin.de, Feb 23, 2007
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. EvgueniB
    Replies:
    1
    Views:
    605
    Anthony Borla
    Dec 15, 2003
  2. Frank Fredstone
    Replies:
    1
    Views:
    424
    Jean-Francois Briere
    Jun 27, 2006
  3. Alan Isaac

    find minimum associated values

    Alan Isaac, Jan 25, 2008, in forum: Python
    Replies:
    8
    Views:
    253
    Alan Isaac
    Jan 25, 2008
  4. Philipp
    Replies:
    21
    Views:
    1,093
    Philipp
    Jan 20, 2009
  5. Mmcolli00 Mom
    Replies:
    4
    Views:
    86
    Jonathan Rochkind
    Jun 16, 2009
Loading...

Share This Page