http://qs1969.pair.com?node_id=436578

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

I have a list containing various names, some with a numeric suffix and some without. For example my list would contain (file1...file20), (server1...server20), & (ps,df,find,etc...). I would like to sort this list first by an alpha comparison, and secondly by a numeric value comparison.

Here's what I have so far:

@pretty=sort { my @a = $a =~ /(\D+)(\d+)/; my @b = $b =~ /(\D+)(\d+)/; $a[0] cmp $b[0] || $a[1] <=> $b[1] } @not_so_pretty;

This is as close as I have come to getting what I need, unfortunately this sort will return the elements of my list that do not contain numeric characters at the top of the list, as opposed to the required method of an alpha comparison first.

Regards, Ev

Replies are listed 'Best First'.
Re: a proper sort
by dragonchild (Archbishop) on Mar 04, 2005 at 14:51 UTC
    You want to change your sort to
    @pretty=sort { my @a = $a =~ /(\D+)(\d+)?/; my @b = $b =~ /(\D+)(\d+)?/; $a[0] cmp $b[0] || ($a[1] || 0) <=> ($b[1] || 0) } @not_so_pretty;

    Right now, your regex will fail to provide anything in @a/@b if there aren't any numbers.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      Brilliant! This works perfectly.

      I had been trying for days to firgure this one out - thank you so much.

Re: a proper sort
by eieio (Pilgrim) on Mar 04, 2005 at 14:56 UTC
    This may work:
    my @pretty = sort { my @a = $a =~ /^(.+?)(\d*)$/; my @b = $b =~ /^(.+?)(\d*)$/; return ( $a[0] cmp $b[0] ) || ( $a[1] && $b[1] && $a[1] <=> $b[1] ) || ( $a[1] && 1 ) || ( $b[1] && -1 ); } @not_so_pretty;
    I changed the regular expressions to ensure that the entire name is captured (^ and $). I also changed them such that words that aren't suffixed by numbers still match (\d*). Finally, although, I'm not sure if this is important for your situation, I changed them such that any characters before the number suffix are allowed (.+?). The return is more complicated since it now handles the case where the $a[1] or $b[1] may not be defined since the word doesn't contain a numeric suffix.

    Update: go with dragonchild's approach which is more concise and clear. However, do consider if any words may contain number other than as a suffix and decide how you want to handle this case (my code sorts these alphabetically).

Re: a proper sort
by Anonymous Monk on Mar 04, 2005 at 15:07 UTC
    This works assuming that the numbers are on the end, there are no leading 0's, and there are at most 5 digits (if more, it's easy to see where to adjust):
    my @pretty = map {s/(?<=\D)0+//; $_} sort map {chomp; do {no warnings; sprintf "%s%05d", /(\D*)(\d* +)/}} @not_so_pretty;
    The first map (that is, the bottom one in the code), normifies the strings, by padding 0's so all the numbers have equal length - if there are no trailing numbers, 0's will be used. Then we perform a regular sort, and finally, we strip off the leading 0's (which could mean all the 0's). This technique is one of the variations of the GRT.
Re: a proper sort
by Roy Johnson (Monsignor) on Mar 04, 2005 at 23:32 UTC
    You might want to have a look at Sort::Naturally.

    Caution: Contents may have been coded under pressure.