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

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

I have a bit of code as follows:
opendir (LOWEST, "<$input_directory_lowest") or die "Error opening d +irectory: $!"; foreach (sort grep(/^Flat_file.*\.txt$/,readdir(LOWEST))) { push @lowest_priority,$_; } closedir LOWEST;
What is the most efficient way of sorting the files in @lowest_priority by time created. I would like to do this in the foreach clause, if this is the best way of doing it.

Replies are listed 'Best First'.
Re: sorting files by date
by davorg (Chancellor) on Feb 05, 2007 at 10:57 UTC

    Look at the three file test operators -M, -A and -C and decide which of them best mirrors your "time created". Let's assume it's -M.

    The naive way to sort a list of files then becomes:

    my @sorted_files = sort { -M $a <=> -M $b } @files;

    But you specifically asked for an efficient method, so you'll probably want to replace that with an Orcish Maneuver or a Schwartzian Transform. See Uri Guttman and Larry Rosler's excellent paper for discussion of these methods and other Perl sorting mechanisms.

    Also, you have this code:

    opendir (LOWEST, "<$input_directory_lowest")

    I think you're confusing open and opendir. for opendir you don't need the '<' on the directory name. In fact, I'm surprised to see that it works.

    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 files by date
by marto (Cardinal) on Feb 05, 2007 at 11:04 UTC
    Win,

    Did you learn nothing from your previous posts Sort files and No. files in folder? The advice that davorg has given you, it would seem you already knew (at lease some of this) based on your code you posted in No. files in folder, to which other monks replied giving you help.

    Martin
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: sorting files by date
by shmem (Chancellor) on Feb 05, 2007 at 13:47 UTC
    opendir (LOWEST, "<$input_directory_lowest")

    Please re-read chromatics answer to one of your previous posts, and follow the advice therein. Read opendir and readdir.

    The syntax for opendir is not the same as for open. You must not specify an open mode via < or >, since directories are never opened for writing.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: sorting files by date
by Moron (Curate) on Feb 05, 2007 at 14:01 UTC
    For efficiency purposes, the trap to avoid is allowing sort to do a -M on the same file each time it lands on that file during the sorting algorithm - the ||= operator can be useful in this regard...
    my %filedate; my @lowest_priority = sort { ( $filedate{ $a } ||= -M $a ) <=> ( $filedate{ $b } ||= -M $b ) } grep /^Flat_file.*\.txt$/, readdir LOWEST;

    -M

    Free your mind

Re: sorting files by date
by Thelonius (Priest) on Feb 06, 2007 at 16:18 UTC
    One thing that everybody here is forgetting is that unless you are doing a readdir on the current directory, you must prepend the directory name for -M, -C, stat, etc.

    Here are some ways to do it:

    #!perl -w use strict; use File::stat; # Method 1: my $input_directory_lowest = "pridir"; opendir (LOWEST, $input_directory_lowest) or die "Error opening directory: $!"; my @files; while (my $fname = readdir LOWEST) { if ($fname =~ /^Flat_file.*\.txt$/) { push @files, [stat("$input_directory_lowest/$fname")->ctime, $fname]; } } closedir(LOWEST); my @low1 = map { $_->[1] } sort {$a->[0] <=> $b->[0]} @files; print join("\n ", "low1=", @low1), "\n"; # Method 2: same as 1, but without explicit loop opendir (LOWEST, $input_directory_lowest) or die "Error opening directory: $!"; my @low2 = map { $_->[1] } sort {$a->[0] <=> $b->[0]} map { [ stat("$input_directory_lowest/$_")->ctime, $_ ] } grep /^Flat_file.*txt$/, readdir(LOWEST); closedir(LOWEST); print join("\n ", "low2=", @low2), "\n"; # Method 3: outputs file names with directory; uses 'glob' my @low3 = map { $_->[1] } sort {$a->[0] <=> $b->[0]} map { [ stat($_)->ctime, $_ ] } glob("$input_directory_lowest/Flat_file*.txt"); print join("\n ", "low3=", @low3), "\n";