Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Natural sort on array 2

Status
Not open for further replies.

Masali

Programmer
Jun 19, 2002
143
SE
I have a filelist like this in an array:

file031.txt
file001.txt
file005.txt
file101.txt
file006.txt

I would like to sort it with the numbers in correct order. How can I do this?

Best regards

Masali
 
In the example that you've given, you don't need to do anything special at all to sort the array, since the numbers are all zero-filled for a length of 3 and the non-numeric parts of the filenames are identical from one to the other except for the numbers. So simply saying @arr = sort @arr; would do it.

However, I have a feeling your real data is not this simple.
Maybe it's more like this:

abc031.txt
xy001.txt
mikey5.txt
file101.txt
file06.txt


Then you could do something like this:
Code:
my @arr = <DATA>;
chomp(@arr);
@arr = sort {sortem()} @arr;
print "$_\n" for @arr;

sub sortem {
    my ($x, $y) = ($a =~ /(\d+)/, $b =~ /(\d+)/);
    $x <=> $y;
}
__DATA__
abc031.txt
xy001.txt
mikey5.txt
file101.txt
file06.txt
or this
Code:
my @arr = <DATA>;
chomp(@arr);

@arr = map {$_->[1]}
       sort {$a->[0] <=> $b->[0]}
       map {[/(\d+)/, $_]} @arr;

print "$_\n" for @arr;

__DATA__
abc031.txt
xy001.txt
mikey5.txt
file101.txt
file06.txt
Both give the following output:
xy001.txt
mikey5.txt
file06.txt
abc031.txt
file101.txt


HTH

 
Dear Mikevh,

could you please explain, how this funcation works.

@arr = sort {sortem()} @arr;

sub sortem {
my ($x, $y) = ($a =~ /(\d+)/, $b =~ /(\d+)/);
$x <=> $y;
}

Thanks
19decsat
 
Hello again!

Thanks for the quick answer. This works fine, but I would like it to sort on the text part first, numbers second. Like this:

abc031.txt
xy001.txt
mikey5.txt
file101.txt
file06.txt

becomes:

abc031.txt
file06.txt
file101.txt
mikey5.txt
xy001.txt
 
Yet another job for my friend Sort::Fields
You can treat the filename as having three fields (text, number and extension). To get those fields, you must split on 'between the last letter and the first number' and the dot. Once that's done, it's trivial to tell Sort::Fields to sort on the first field textually and then numerically on the second. This gives:
Code:
use Sort::Fields;
                                                                               
my @sorted = fieldsort '(?<=\D)(?=\d)|\.', [ 1, '2n' ], @unsorted;
 
masali, you could get the effect you want (sort on the alpha part preceding the numbers, then the numbers) this way:
Code:
@arr = map {$_->[2]}
       sort {$a->[0] cmp $b->[0] || $a->[1] <=> $b->[1]}
       map {[/^(\D+)/, /(\d+)/, $_]} @arr;
19decsat, the way the sortem() function works in the earlier example:
my ($x, $y) = ($a =~ /(\d+)/, $b =~ /(\d+)/);
The right-hand side creates a list of the numeric parts of $a and $b, which represent 2 adjacent elements of @arr; these are assigned to $x and $y on the left-hand side; the "spaceship" operator (<=>) compares them numerically and returns -1 (less than), 0 (equal), or 1 (greater than), which is the value returned by the function.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top