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

Trying to create a Subroutine to sort array based on any column numerically when my array is having both characters and numercis Below code doesnt seem to work

#!/usr/bin/perl $objd=sprintf "%10.0f %10.0f %10.0f %10.0f %10.0f", "15","4","5","6"," +7"; $objd2=sprintf "%10.0f %10.0f %10.0f %10.0f %10.0f", "8","9","10","11" +,"12"; push @numbers,$objd; push @numbers,$objd2; sub sort_array { @unsorted_array = @{$_[0]}; $col_2sort = $_[1]; ## Implementing Schwartzian Transform algorithm to sort in fas +test way @sorted_array= map { # Get original line back $_->[0] } sort { # Compare input fields $b->[$col_2sort] <=> $a->[$col_2sort] } map { # Turn each line into [original line, input +field] [ $_, (split " ", $_)[$col_2sort] ] } @unsorted_array; } printf "UN-Sorted Array\n"; foreach my $line ( @numbers ) { printf "$line\n"; } @sorted_array= sort_array(\@numbers,2); printf "\n\n\n"; printf "Sorrted Array\n"; foreach my $line ( @sorted_array ) { printf "$line\n"; }

Replies are listed 'Best First'.
Re: Sorting based on any column
by Anonymous Monk on May 19, 2015 at 10:36 UTC

    Look at your sort criteria: { $b->[$col_2sort] <=> $a->[$col_2sort] }. $col_2sort is 2, but that array element does not exist. Change the sort criteria to { $b->[1] <=> $a->[1] } and it works.

    You really should be using strict and warnings, using them will help clean up your code.

Re: Sorting based on any column
by smknjoe (Beadle) on May 19, 2015 at 14:36 UTC
    Can you please give a before/after data example of what you are trying to accomplish? Your question seems a little vague and your input data doesn't lend much help, as column #2 is (4,9) and looks to already be sorted correctly. And yes - as previous poster indicated, "use strict;" and "use warnings;" should be in every Perl script.
      column #2 is (4,9) and looks to already be sorted correctly

      Off by one - index 2 is the third column, (5,10). Also the OP appears to want a reverse sort - at least I'm assuming that based on the $b being before $a and the OP saying the current code doesn't work.

Re: Sorting based on any column
by perlomatic (Initiate) on May 19, 2015 at 21:58 UTC

    >Trying to create a Subroutine to sort array based on any column numerically when my array is having both characters and numercis

    I am not sure I completely understand the example...but this will sort alpha and numeric

    #! /usr/local/bin/perl -w use strict; my $data = [qw| z x y 2 1 3 |]; print 'no sort:' , @$data ,"\n"; print 'sort: ' , (sort _sort_a_tron @$data) , "\n"; sub _sort_a_tron { no warnings; return ( $a <=> $b or $a cmp $b ); } # output # # no sort:zxy213 # sort: xyz123
      This is basically one of the solutions I offered above, but with a number of caveats about the data.

        Sorry for not being clear in requirement. Consider my OBJD Array data is like shown below:

        1 ab 2 3 cd 4 5 6 6 9 rc 4 5 ef 6 3 4 1 7 fa 5 2 tg 5 9 9 0 3 bg 3 9 jh 5 2 2 1

        I want to create a subroutine which takes two argument as input, First argument is array and second argument is column number on which sorting has to be done. Say for example sort_array is a subroutine and I pass OBJD array and column to sort as arguments. something like sort_array(\@OBJD,2) and this should provide me below output

        1 ab 2 3 cd 4 5 6 6 3 bg 3 9 jh 5 2 2 1 9 rc 4 5 ef 6 3 4 1 7 fa 5 2 tg 5 9 9 0

        Or something like sort_array(\@OBJD,6) and this should provide me below output

        3 bg 3 9 jh 5 2 2 1 9 rc 4 5 ef 6 3 4 1 1 ab 2 3 cd 4 5 6 6 7 fa 5 2 tg 5 9 9 0

        Would like to do it using regular sorting method as well as using 'Schwartzian transform' just to learn it.

Re: Sorting based on any column
by Anonymous Monk on May 19, 2015 at 17:24 UTC
    Seems to me that the ability to transparently use <=> (numeric) vs. cmp (string), as-appropriate, is a major part of the question . . . ?
      Yes, it seems important, if I understand correctly this OP' quote: "my array is having both characters and numercis".

      I can see two ways to go about it. Presumably, when using this function, the user knows what there will be in the column, so that the type of comparison could a parameter passed to the function. Assuming this is Boolean parameter stored in the $numeric variable, it could lead to something like this (untested):

      @sorted_array= map { $_->[0] } sort { $numeric ? $b->[1] <=> $a->[1] : $b->[1] cm +p $a->[1]} map { [ $_, (split " ", $_)[$col_2sort] } @unsorte +d_array;
      The other way might possibly look simpler (but is probably less clean and more error-prone):
      @sorted_array= map { $_->[0] } sort { $b->[1] <=> $a->[1] || $b->[1] cmp $a->[1] +} map { [ $_, (split " ", $_)[$col_2sort] } @unsorte +d_array;
      This is based on the idea that comparing two strings with <=> will yield 0, a false result, so that, if we have strings, the cmp comparison will be executed and return the correct comparison. But there are at least two downsides with this approach:

      1. You need to silence out the Argument "foo" isn't numeric in numeric comparison (<=>) at ... warning with the appropriate pragma, and I usually hesitate quite a bit before deciding to silence out any warning (except perhaps, but only very very rarely, the initialized and the deep recursion warnings when I really know for sure it is OK);

      2. There are several edge cases where it might break, especially if you have one numeric argument and one non-numeric argument:

      $ perl -e 'print 3 <=> "b"' 1 ~ $ perl -e 'print 3 cmp "b"' -1
      So this could work out for a one-off sort if you know your data very well, but this is really not very robust production code.

      Update: Corrected two stupid typos in my code, thanks to AnomalousMonk for proofreading.

      What on earth are you talking about? What does cmp have anything to do with the question?

Re: Sorting based on any column
by Anonymous Monk on May 19, 2015 at 19:52 UTC
      Maybe applicable

      No, it's not, as OP is only working with numbers (in the Perl sense), not strings.

        Ack, apologies, I spent too much time looking at the code and not enough reading the question. (In my defense, the OP's sample data isn't really representative then :-) )