srdst13 has asked for the wisdom of the Perl Monks concerning the following question:

Here is what I would like to do:
@vector = qw( 27 32 46 5 102 76 ); @order = order_them(@vector);
so that @order contains:

2,3,4,1,6,5

and
@vector[@order] = 5,27,32,46,76,102
I need to build the function "order_them". I have a feeling this is probably a perl one-liner or close to it, but I just can't figure out what is in that one line.

Thanks,
Sean

Replies are listed 'Best First'.
Re: Getting the order of an array
by BrowserUk (Patriarch) on May 23, 2005 at 19:24 UTC

    my @vector = qw( 27 32 46 5 102 76 ); my @order = sort{ $vector[ $a ] <=> $vector[ $b ] } 0 .. $#vector; print @vector[ @order ]; 5 27 32 46 76 102

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
      Note that (this is more to OP) the OP said he wanted (or just expected) @order to contain 2,3,4,1,6,5 but in this solution (which i believe is correct) @order contains 3,0,1,2,5,4 ... Note especially the 1-based vs 0-based difference which is necessary for the array slice to work.

        In addition to 1-based, his numbering implied right to left ordering. The 1-based ordering could be accomodated:

        { local $[=1; @order = sort{ $vector[ $a ] <=> $vector[ $b ] } 1 .. $#vector; print @order; } 4 1 2 3 6 5

        though I wouldn't advise it. But accomating the reversal of the natural ordering without requiring sticking in unnecessary reverse calls all over the place was more of a problem.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: Getting the order of an array
by dragonchild (Archbishop) on May 23, 2005 at 19:22 UTC
    You mean you want to "Sort the indices of an array based on the values of that array?" That should help you out. And, yes, it is a one-liner.

    • In general, if you think something isn't in Perl, try it out, because it usually is. :-)
    • "What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?"
Re: Getting the order of an array
by NetWallah (Canon) on May 23, 2005 at 21:34 UTC
    Another way of getting close to the OP's desired results:
    UPDATE:Finally figured what the OP was trying for:
    - he needs both ORDER and RANK.
    my @vector = qw( 27 32 46 5 102 76 ); my @order = map {$_+1} sort{ $vector[ $a ] <=> $vector[ $b ] } 0 .. $#vector; my ($r,@rank); $rank[$_ - 1]= ++$r for @order; unshift @vector,undef; print qq(@rank\n@vector[@order]\n) __Output__ 2 3 4 1 6 5 5 27 32 46 76 102
    The OP's definition of the sequence of @order was probably wrong.
    The code looks funky because the OP seems to want a lower-bound of 1 instead of zero, for the @vector array.

         "There are only two truly infinite things. The universe and stupidity, and I'm not too sure about the universe"- Albert Einstein

Re: Getting the order of an array
by 5mi11er (Deacon) on May 23, 2005 at 20:20 UTC
    I'm also quite confused as the actual order of @vector according to the OP's @order would actually be (assuming 1 based indexes) 32, 46, 5, 27, 76, 102. This certainly isn't ordered numerically...

    Update: From this post below, rank is a much better description than order. NetWallah++ for figuring this out.

    -Scott

Re: Getting the order of an array
by tall_man (Parson) on May 23, 2005 at 21:09 UTC
    To get the OP's desired output, one needs another loop such as the one below:
    use strict; my @vector = qw( 27 32 46 5 102 76 ); my @order = sort{ $vector[ $a ] <=> $vector[ $b ] } 0 .. $#vector; my $i = 1; my @order_it; foreach (@order) { $order_it[$_] = $i++; } print join(' ',@order_it),"\n"; print "@vector[ @order ]\n";

    Update: Updated to show the correct way to get the second part of the OP's answer. In answer to BrowserUk's comment, I took the original question to mean that both outputs are required but that the OP didn't realize they could not both be supported by a single array.

      I guess it depends upon whether you feel the intermediate ordering or the final ordering specified is the crux of what the OP is trying to achieve?

      use strict; my @vector = qw( 27 32 46 5 102 76 ); my @order = sort{ $vector[ $a ] <=> $vector[ $b ] } 0 .. $#vector; my $i = 1; my @order_it; foreach (@order) { $order_it[$_] = $i++; } print join(' ',@order_it),"\n"; print "@vector[ @order_it ]\n"; __END__ P:\test>junk 2 3 4 1 6 5 Use of uninitialized value in join or string at P:\test\junk.pl line 1 +1. 46 5 102 32 76

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.