http://www.perlmonks.org?node_id=778819

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

I have a array like this.
@array=("ch1","ch11","ch2","ch5","ch55","ch16");
i need to sort like
ch1
ch2
ch5
ch11
ch16
ch55

How can i do that?

Replies are listed 'Best First'.
Re: sorting an array
by moritz (Cardinal) on Jul 10, 2009 at 07:10 UTC
    A reply falls below the community's threshold of quality. You may see it by logging in.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: sorting an array
by johngg (Canon) on Jul 10, 2009 at 09:50 UTC

    A solution using a Schwartzian Transform.

    knoppix@Knoppix:~$ perl -le ' @arr = qw{ ch1 ch11 ch2 ch5 ch55 ch16 }; print for map { $_->[ 0 ] } sort { $a->[ 1 ] <=> $b->[ 1 ] } map { [ $_, ( split m{(?<=\D)(?=\d)} )[ 1 ] ] } @arr;' ch1 ch2 ch5 ch11 ch16 ch55 knoppix@Knoppix:~$

    I hope this is of interest.

    Cheers,

    JohnGG

Re: sorting an array
by 1Nf3 (Pilgrim) on Jul 10, 2009 at 07:40 UTC

    Sort::Key::Natural seems the best way here, so I'd stick with moritz's suggestion.

    But if you want to experiment with sort, it can be done with a custom sort subroutine. An example (tested):

    #!/usr/bin/perl -w use strict; sub by_the_number_at_the_end { $a =~ /(\d+)$/; my $end_of_a = $1; $b =~ /(\d+)$/; my $end_of_b = $1; return -1 if $end_of_a < $end_of_b; return 0 if $end_of_a == $end_of_b; return 1 if $end_of_a > $end_of_b; } my @array=("ch1","ch11","ch2","ch5","ch55","ch16"); print join ("\n", sort by_the_number_at_the_end @array);

    Regards,
    Luke

Re: sorting an array
by ELISHEVA (Prior) on Jul 10, 2009 at 08:08 UTC

    Sort::Key::Natural will do the trick assuming you can use CPAN (see Yes, even you can use CPAN for tips on how), but there are a couple concepts here that are probably worth learning in any case:

    sort lets you define a custom sort routine, like this:
    use strict; use warnings; my @aChapters=("ch1","ch11","ch2","ch5","ch55","ch16"); my @aSorted = sort { my ($sA, $iA) = ($a =~ /(^[^\d]+)(\d+)/); my ($sB, $iB) = ($b =~ /(^[^\d]+)(\d+)/); my $x = $sA cmp $sB; ($x = $iA <=> $iB) unless $x; $x; } @aChapters; local $"="\n"; print "@aSorted";

    $a and $b are special variables that represent the two members of the list you want to sort. The main trick here is to (i) split your name into two parts: an alpha part and a numeric part (ii) compare the alpha part using the alpha comparison operator (cmp) and the numeric part using the numeric comparison operator (<=>). See perlop for more information.

    Best, beth

Re: sorting an array
by cdarke (Prior) on Jul 10, 2009 at 10:53 UTC
    Given the simple nature of the data, i.e. each element startswith 'ch', I'm surprised others didn't do this:
    my @sorted = sort { substr($a,2) <=> substr($b,2) } @array;
    OK, so it's calling substr rather a lot of times, so with a large array that might be slow. Alternatively we could cache it:
    @array = map {substr($_,2)} @array; my @sorted = sort { $a <=> $b } @array; @sorted = map { "ch$_" } @sorted;
Re: sorting an array
by JavaFan (Canon) on Jul 10, 2009 at 10:59 UTC
    map {"ch$_"} sort {$a <=> $b} map {/(\d+)/} @array
    should do.
Re: sorting an array
by missingthepoint (Friar) on Jul 10, 2009 at 09:09 UTC
    # first num sub num_from_str { my $str = shift; my ($num) = $str =~ /(\d+)/; return $num; } my @a = qw( ch1 ch11 ch55 ch5 ch2 ch16 ); my @sorted = sort { num_from_str($a) <=> num_from_str($b) } @a; print "$_\n" for @sorted;

    update: ... but Sort::Key::Natural is a more complete solution, because it sorts based on the alphabetical bits as well.


    The zeroeth step in writing a module is to make sure that there isn't already a decent one in CPAN. (-- Pod::Simple::Subclassing)
Re: sorting an array
by pKai (Priest) on Jul 10, 2009 at 11:59 UTC