Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Sorting Files with Numbers

by Anonymous Monk
on Dec 30, 2002 at 15:42 UTC ( [id://223046]=perlquestion: print w/replies, xml ) Need Help??

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

I have a list of files:
file1
file2
...
file10
...
file20
...

The list can contain any number of file names up to about 1000. I have these names stored in a hash, and use the following code to print the file names:
foreach my $file (sort {$a cmp $b} keys %hash) { print "$key \n"; }
problem is the the filenames print like this:
file1
file10
file11
file2
file20
file3

How do I sort them based on counting order? I don't have access to install any modules that are not already installed on the system, so please don't suggest a module.

Thanks in Advance.

Replies are listed 'Best First'.
Re: Sorting Files with Numbers (natural sort)
by tye (Sage) on Dec 30, 2002 at 15:46 UTC
Re: Sorting Files with Numbers
by dragonchild (Archbishop) on Dec 30, 2002 at 15:47 UTC
    You need to make your own custom sorting routine. Try something along the lines of:
    # Note that $a and $b are special globals. DO NOT my THEM!!! sub my_sort { my ($a_word, $a_num) = $a =~ /^(\w+)(\d+)$/; my ($b_word, $b_num) = $b =~ /^(\w+)(\d+)$/; return $a_word cmp $b_word || $a_num <=> $b_num; } foreach my $file (sort my_sort keys %hash) { print $key, $/; }

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: Sorting Files with Numbers
by jdporter (Paladin) on Dec 30, 2002 at 15:56 UTC
    The key is to extract the numeric part of each filename, and compare numerically those values.
    Something like this will do the job:
    foreach my $file ( sort { my($as,$an) = $a =~ /^(\D*)(\d+)/; my($bs,$bn) = $b =~ /^(\D*)(\d+)/; $as cmp $bs or $an <=> $bn } keys %hash )
    However, that is not optimal for the sort.
    You can resort (heh) to a Schwartzian Transform to improve efficiency:
    foreach my $file ( map { $_->[0] } sort { $a->[1] cmp $b->[1] or $a->[2] <=> $b->[2] } map { [ $_, /^(\D*)(\d+)/ ] } keys %hash )

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

Re: Sorting Files with Numbers
by BrowserUk (Patriarch) on Dec 30, 2002 at 15:49 UTC

    Try this. (untested)

    foreach my $file (sort {substr($a,4) <=> substr($b,4)} keys %hash) { print "$key \n"; }
    This compares everything except the first 4 characters using the numeric comparision operator. <=>.

    Examine what is said, not who speaks.

      I'm guessing that 'file' was just an example of a name that the orginal poster was using. It isn't very likely that the filenames will be exactly 4 chars long followed by a number. This will probably work better (untested):

      sub file_sort { $a =~ /(\d*)$/; my $a_num = $1; $b =~ /(\d*)$/; my $b_num = $1; $a_num <=> $b_num; } foreach my $file (sort &file_sort keys %hash) { print "$key \n"; }

        Your solution fails to sort by the alpha prefix first. See dragonchild's answer, above, which groups files first by their alpha prefix, then numerically by suffix.

        Update: jdporter's solution (here) might be even more efficient with this dataset, since it uses \D+ (non-digits) rather than \w+ (word characters) to match the prefix, but it will also match punctuation and other non-alpha characters (which may not be what you want).

Re: Sorting Files with Numbers
by Wonko the sane (Deacon) on Dec 30, 2002 at 16:11 UTC
    If all of your file names are essentially the same, except for the numeric part,
    then all you should really have to do is change your comparison operator.

    foreach my $file (sort {$a <=> $b} keys %hash) { print "$key \n"; }

    This will work even if the Numeric part of the file is in the middle of the file name.

    Ex.

    perl -e 'print qq{[$_]\n} for ( sort { $a <=> $b } qw(file1 file10z fi +le11s file2q file20z file21f) )'
    Wonko.
      No, it won't, since numberification of the string only looks at the beginning. It will see them all as zero and compare equal in every case.

      Change the order of the data in your example, since they were sorted to begin with.

      [F:\]perl -e "print qq{[$_]\n} for ( sort { print 0+$a, $b, $/; $a <=> + $b } qw(file2 file20z file22s file1q file10z file12f) )" 00 00 00 00 00 [file2] [file20z] [file22s] [file1q] [file10z] [file12f]
      —John
        Ugh, your right. Thank you for pointing this out to me.
        I like the method you used to show this.

        Wonko.

Re: Sorting Files with Numbers
by Anonymous Monk on Jan 01, 2003 at 21:55 UTC
    Amazingly, no one has mentioned Sort::Naturally. Hit CPAN and enjoy.

    Cheers,
    -Anomo
Re: Sorting Files with Numbers
by helgi (Hermit) on Jan 02, 2003 at 11:05 UTC
    See other replies for solutions to your sorting problem.

    What I wish to respond to is this nonsense:

    I don't have access to install any modules that are not already installed on the system, so please don't suggest a module.

    You certainly can install any modules you like, "access" or no "access". You just can't make them available to others unless you have some sort of root access.

    Read the FAQ answer:

    perldoc -q "How do I keep my own module/library directory?"
    
    for details.

    --
    Regards,
    Helgi Briem
    helgi AT decode DOT is

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://223046]
Front-paged by tye
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-03-28 09:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found