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

Hi Monks,

How can I restore the original order of an array after sorting and applying a function on the values of an array? For instance:

@myarray = (2.5 ,4.8, 3.2, 8.5, 8.2, 12.5);

Sort numerically in a descending order.

@myarraydecsend(12.5, 8.5, 8.2, 4.8, 3.2, 2.5)

Multiply each number by 6 (array size) and divide the results by array size at each iteration array size decreases by 1 (count down). like the example

@myopperatedarray( 12.5*6/6, 8.5*6/5, 8.2*6/4 …..2.5*6/1)

You would get (12.5, 10.2, 12.3, 9.6, 9.6, 15)

map to original data [6] [4] [5] [2] [3] [1]

Now restore the original order.Something like hash key sort to get:

(15, 9.6 , 9.6, 10.2, 12.3, 12.5)

  • Comment on Restore the original order of an array after sort and performing some funchtion on array values
  • Download Code

Replies are listed 'Best First'.
Re: Restore the original order of an array after sort and performing some funchtion on array values
by BrowserUk (Patriarch) on Mar 03, 2010 at 17:29 UTC

    Sort the indexes and apply your operations in that order without actually changing the ordering of the array:

    #! perl -slw use strict; my @array = (2.5 ,4.8, 3.2, 8.5, 8.2, 12.5); my @orderedIndexes = sort{ $array[ $b ] <=> $array[ $a ] } 0 .. $#array; my $n = @array; $array[ $_ ] *= @array / $n-- for @orderedIndexes; print "@array"; __END__ c:\test>junk68 15 9.6 9.6 10.2 12.3 12.5

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Definitely the way to go.

      For a lot of stuff, perl's list munging capabilities are much better/straightforward than dealing with array indices.

      The problem at hand is one of the few instances to NOT take advantage of perl's list-centric features and work with the indices directly.

      -Greg

      Thanks For the quick response. To simplify my question I used an array as an example.

      Can I use the same concept on a hash value? Let's say I want to perform the same operation on values of the hash and return the sorted keys with new data. For instance:

      #!/usr/local/bin/perl use strict; my %pvalues = ( 1=> 0.5453980, 2=> 0.4902384, 3=> 0.8167950, 4=> 0.2821822, 5=> 0.4693030, 6=> 0.6491767, 7=> 0.9802138, 8=> 0.1155778, 9=> 0.9585124, 10=> 0.4069490 );

      They keys of course are unique but the values may not. I have done the following.

      my $numberPvalues = scalar keys %pvalues; my $numberPvaluesCounter = scalar keys %pvalues; my $numberPos = 0; # now loop through all p values and calculate FDR value # store FDR values in hash, key = pos, value is FDRq my @reversPvalues =(); my %sortedTransfPvalues =(); my @inversPvalue =(); my @cumulminPvalue =(); while ($numberPvaluesCounter >= 1 ){ #Sort the original pvalue hash based on the values descen +ding manner foreach my $posCurrentPvalue (sort {$pvalues{$b} <=> $pva +lues{$a}} keys %pvalues) { #calculate the transformed pvalues based on its rank + in the sorted value in the hash my $transformedPvalue = $pvalues{$posCurrentPvalue} +* $numberPvalues / $numberPvaluesCounter; $numberPvaluesCounter--; #new indcies $numberPos++; push (@inversPvalue, $transformedPvalue); #$sortedTransfPvalues{$numberPos}{$posCurrent +Pvalue} = $transformedPvalue; #$sortedTransfPvalues{$posCurrentPvalue}{$num +berPos} = $transformedPvalue; print "$posCurrentPvalue\t$transformedPvalue\ +n"; }#end foreach and sorting values }#end while for count down
        Can I use the same concept on a hash value?

        Yes. In exactly analogous fashion. Order the keys by value and then process your values in that order:

        #!/usr/local/bin/perl use strict; use Data::Dump qw[ pp ]; my %pvalues = ( 1=> 0.5453980, 2=> 0.4902384, 3=> 0.8167950, 4=> 0.2821822, 5=> 0.4693030, 6=> 0.6491767, 7=> 0.9802138, 8=> 0.1155778, 9=> 0.9585124, 10=> 0.4069490 ); my @orderedKeys = sort { $pvalues{ $b } <=> $pvalues{ $a } } keys %pvalues; my $d = my $n = values %pvalues; $pvalues{ $_ } *= $n / $d-- for @orderedKeys; pp \%pvalues; __END__ c:\test>junk68 { 1 => "0.908996666666667", 2 => "0.9804768", 3 => "1.02099375", 4 => "1.410911", 5 => "1.1732575", 6 => "0.927395285714286", 7 => "0.9802138", 8 => "1.155778", 9 => "1.06501377777778", 10 => "1.35649666666667", }

        Of course, hash keys aren't intrinsically ordered, so you'll have to order them as needed for output, but one of the structure dumpers will do that for you.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Restore the original order of an array after sort and performing some funchtion on array values
by salva (Canon) on Mar 03, 2010 at 17:37 UTC
    Instead of the array elements sort indexes...
    my @ix = sort { $myarray[$b] <=> $myarray[$a] } 0..$#myarray; $div = @myarray; $myarray[$_] *= $div-- / @myarray for @ix; print "@myarray\n"
    ...or even...
    my $div; $myarray[$_] *= ++$div / @myarray for sort { $myarray[$a] <=> $myarray +[$b] } 0..$#myarray;
Re: Restore the original order of an array after sort and performing some funchtion on array values
by kennethk (Abbot) on Mar 03, 2010 at 17:26 UTC
    Are your values in @myarray unique? This has a direct impact since the simplest way to maintain a mapping in Perl is using a hash where keys must be unique. If they are, you can accomplish all you have above with:

    #!/usr/bin/perl use strict; use warnings; my @myarray = (2.5 ,4.8, 3.2, 8.5, 8.2, 12.5); my %value_map; my $counter = @myarray; for my $key (sort {$b <=> $a} @myarray) { $value_map{$key} = $key*6/$counter--; } for my $key (@myarray) { print "$key\t-> $value_map{$key}\n"; }

    Note I have not had to cache any intermediate results - you certainly could if you wanted too, so long as you do not change @myarray.

    If the values are not unique, I think the most prudent approach is using two hashes keyed equivalently, probably with 1 .. @myarray.If your array values are not guaranteed unique, sorted indices as per BrowserUK's suggestion is a stronger choice.

Re: Restore the original order of an array after sort and performing some funchtion on array values
by davidnicol (Acolyte) on Mar 03, 2010 at 22:29 UTC
    you want to look at hash slice syntax. Make a hash table mapping the original to the transform like so:
    @hash{@original} = @original
    work with them in the order you want
    dosomethingto($_) for @hash{@sortedoriginal}
    then get the transformed results as
    @hash{@original}