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

hi

here is my problem i have a series of indexes which i would like to sort, but using this code :

use strict; my @g = qw(St1 St10 St2 St12 St6 St8); my @b = sort{$a cmp $b} @g; foreach my $x (@b){ print "$x\n"; }
the result is : St1 St10 St12 St2 St6 St8 but what i need is :

St1 St2 St6 St8 St10 St12

i mean i don't know how to sort a set of strings that have numbers in it . i mean i understand why this isn't working by the method i used but if any one has any suggestions on how to overcome this problem ...

thanx

Replies are listed 'Best First'.
Re: sorting an array mix
by salva (Canon) on Oct 08, 2008 at 15:22 UTC
Re: sorting an array mix
by Fletch (Bishop) on Oct 08, 2008 at 15:24 UTC

    Presuming that "St" is a static prefix just remove it and compare numerically; if it's not, then break your items into pieces and do a multi-comparison sort comparitor.

    my @sorted = sort { substr( $a, 2 ) <=> substr( $b, 2 ) } @g;

    And search for "schwartzian transform" for hints how to do so efficiently.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: sorting an array mix
by FunkyMonk (Bishop) on Oct 08, 2008 at 21:55 UTC
    In the interests of completeness, here's an ST implementation (update: with alphabetic case folding)
    my @g = qw(St1 St10 St2 St12 St6 St8 ab3 ab0 st5); my @b = map { $_->[2] } sort{ lc $a->[0] cmp lc $b->[0] || $a->[1] <=> $b->[1] } map { [/(\D*)(\d*)/, $_] } @g; foreach my $x (@b){ print "$x\n"; }
    See Schwartzian Transform and Advanced Sorting - GRT - Guttman Rosler Transform for more info

    Unless I state otherwise, all my code runs with strict and warnings
Re: sorting an array mix
by JavaFan (Canon) on Oct 08, 2008 at 15:35 UTC
    People will suggest to use an ST, but I prefer to use a GRT. In this case:
    my @g = qw(St1 St10 St2 St12 St6 St8); my @b = map {s/\D\K0+//; $_} sort map {sprintf "%s%02d", /(\D+)(\d+)/} @g;
    Just make sure you use enough zeros in your sprintf.

    The "trick" here is that we pad the numbers with zeros so the numbers have equal length; then we sort, and finally, we remove the leading zeros.

    Of course, if the data you're going to sort may have leading 0s, you'll have to take care of it.

        You can dodge the zeros issue and make it a bit more efficient using pack
        Yes, up to a point. It'll fail if one of the numbers exceed 4294967295 (2^32, the maximum number that will fit in an unsigned long).

        It also assumes that all the strings start with the same letters, or that if they don't, the non-digit prefix is the secondary key; my solution assumes it's the primary key.

        That is, given the list

        @g = qw [foo11 baz39 bar23 baz7 bar13 foo23];
        your solution sorts it as:
        baz7 foo11 bar13 bar23 foo23 baz39
        and mine as
        bar13 bar23 baz7 baz39 foo11 foo23
        From the example the OP gives, it's not clear what he wants (or even whether the difference is relevant).