sort function, in non-standard cases

A

alexxx.magni

Hi everybody,
I need some hints on how to setup the sort function in a couple of
cases that happened to me recently

<disclaimer>I already did perldoc -f sort, yet I found mainly
discussed numerical/alphabetical cases</disclaimer>

The two sort problems I had - neither of which I was able to solve -
are the following:

1) given in @a a list of files/directories (e.g. returned by
File::Find), sort it directories first, then symlinks, then common
files.
The nearer I came was:

@a=sort {my $x= -d $a; my $y= -d $b; return $x
<=> $y} @a;

but it doesnt work.

2) sort @a, based on the existence/non-existence of the hash value
$c{$a}, where $a's are the elements of @a.

Both the problems are related to my difficulty of translating them in
some kind of comparison between two generic elements $a vs $b, as in
the typical $a<=>$b

Any hint is welcome!

Alessandro Magni
 
J

Joe Smith

Hi everybody,
I need some hints on how to setup the sort function in a couple of
cases that happened to me recently

<disclaimer>I already did perldoc -f sort, yet I found mainly
discussed numerical/alphabetical cases</disclaimer>

It is unfortunate that `perldoc -f sort` does not mention "Schwartzian Transform".
http://en.wikipedia.org/wiki/Schwartzian_transform
sort it directories first, then symlinks, then common files.

#!/usr/bin/perl
# Purpose: Outputs directory listing, dirs first, then symlinks, then rest
use strict; use warnings;

push @ARGV,(glob '*') unless @ARGV;
my @typed_names =
map {substr $_,1}
sort
map { -l $_ ? "l$_" : -d $_ ? "d$_" : "r$_"}
@ARGV;
print join("\n",@typed_names),"\n";

-Joe
 
J

Joe Smith

Joe said:
#!/usr/bin/perl
# Purpose: Outputs directory listing, dirs first, then symlinks, then rest
use strict; use warnings;

push @ARGV,(glob '*') unless @ARGV;
my @typed_names =
map {substr $_,1}
sort
map { -l $_ ? "l$_" : -d $_ ? "d$_" : "r$_"}
@ARGV;
print join("\n",@typed_names),"\n";

Another way:

sub sort_dir3 {
my %dir;
my @res;
foreach my $ent (@_) {
my $key = -l $ent ? 'Link' : -d $ent ? 'Directory' : 'Regular';
push @{$dir{$key}},$ent;
}
push @res, "\t\t$_\n",join("\n",sort @{$dir{$_}}),"\n\n" for sort keys %dir;
@res;
}

-Joe

P.S. The test for -l() before testing for -d() is deliberate. Unless you
want symlinks to directories to show up as directories instead of as symlinks.
 
A

anno4000

Hi everybody,
I need some hints on how to setup the sort function in a couple of
cases that happened to me recently

<disclaimer>I already did perldoc -f sort, yet I found mainly
discussed numerical/alphabetical cases</disclaimer>

The two sort problems I had - neither of which I was able to solve -
are the following:

1) given in @a a list of files/directories (e.g. returned by
File::Find), sort it directories first, then symlinks, then common
files.
The nearer I came was:

@a=sort {my $x= -d $a; my $y= -d $b; return $x
<=> $y} @a;

but it doesnt work.

That is a typical candidate for a Schwartz transform. To each file,
assign a rank of 0, 1, or 2 according as it is a directory, a symlink
or anything else. Then sort according to rank, breaking ties by
comparing the file names:

my @sorted_files = map $_->[ 0] =>
sort { $a->[ 1] <=> $b->[ 1] || $a->[ 0] cmp $b->[ 0] }
map {
my $rank = 2;
$rank = 0 if -d;
$rank = 1 if -l;
[ $_, $rank];
}
@files;

Anno
 
A

anno4000

Joe Smith said:
It is unfortunate that `perldoc -f sort` does not mention "Schwartzian
Transform".
http://en.wikipedia.org/wiki/Schwartzian_transform


#!/usr/bin/perl
# Purpose: Outputs directory listing, dirs first, then symlinks, then rest
use strict; use warnings;

push @ARGV,(glob '*') unless @ARGV;
my @typed_names =
map {substr $_,1}
sort
map { -l $_ ? "l$_" : -d $_ ? "d$_" : "r$_"}
@ARGV;
print join("\n",@typed_names),"\n";

That's not strictly a Schwartz transform but a Guttman-Roesler
transform (GRT). A Schwartz transform builds a list of anonymous
arrays that hold the sort key(s) and the original elements and
uses a sort block to access the keys. A GRT (like your solution)
prepends a single sort key in string form and uses the default sort.

Anno
 
M

Michele Dondi

my @typed_names =
map {substr $_,1}
sort
map { -l $_ ? "l$_" : -d $_ ? "d$_" : "r$_"}
@ARGV;

++ x 10**3! Strictly speaking this is not an ST, which you mention,
but even a GRT, which is better in many respcts. I would have gone ST,
but your solution is more compact, elegant, and most probably fast.
Best code contribution I've seen in some time! FYI, I'm reporting it
elsewhere...


Michele
 
J

John W. Krahn

Joe said:
#!/usr/bin/perl
# Purpose: Outputs directory listing, dirs first, then symlinks, then rest
use strict; use warnings;

push @ARGV,(glob '*') unless @ARGV;
my @typed_names =
map {substr $_,1}
sort
map { -l $_ ? "l$_" : -d $_ ? "d$_" : "r$_"}
@ARGV;
print join("\n",@typed_names),"\n";

You only need to stat the files once:

my @typed_names =
map {substr $_,1}
sort
map { -l $_ ? "l$_" : -d _ ? "d$_" : "r$_"}
@ARGV;



John
 
J

John W. Krahn

That is a typical candidate for a Schwartz transform. To each file,
assign a rank of 0, 1, or 2 according as it is a directory, a symlink
or anything else. Then sort according to rank, breaking ties by
comparing the file names:

my @sorted_files = map $_->[ 0] =>
sort { $a->[ 1] <=> $b->[ 1] || $a->[ 0] cmp $b->[ 0] }
map {
my $rank = 2;
$rank = 0 if -d;
$rank = 1 if -l;
[ $_, $rank];
}
@files;

You only need to stat the files once:

my @sorted_files = map $_->[ 0] =>
sort { $a->[ 1] <=> $b->[ 1] || $a->[ 0] cmp $b->[ 0] }
map {
my $rank = 2;
$rank = 0 if -d;
$rank = 1 if -l _;
[ $_, $rank];
}
@files;



John
 
U

Uri Guttman

JS> Thanks for the link.

and Sort::Maker implements the GRT as well as the ST and other sort
styles. it will make sorts like yours much easier to create.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top