I\'ve been looking everywhere for an answer to this, and I just can\'t get it to work.
I have an input file that is read into an array using Perl. The file is a text
In your last code example you can replace
my @sorted = sort { $b->[4] <=> $a->[4] } @input;
with
my @sorted = sort { (split(' ', $b))[4] <=> (split(' ', $a))[4] } @input;
or even
my @sorted = sort { (split(/\s+/, $b))[4] <=> (split(/\s+/, $a))[4] } @input;
if input data has no lines with leading spaces.
In case this helps folks dropping by in the future - here are some inelegant attempts to sort()
the content of lines.txt
(data from question), by its fifth column, with a Perl one-liner. This should work:
perl -E 'say "@$_" for sort {$a->[4] <=> $b->[4]} map {[(split)]} <>' file
This is more or less the same thing but with the split
"automated" with the autosplit (-a
) switch which creates the @F
array:
perl -anE 'push @t,[@F]}{say "@$_" for sort {$a->[4] <=> $b->[4]} @t' file
If the split pattern is not white space, you can substitute it for the default (\s+
) shown here:
perl -E 'say sort {(split(/\s+/,$a))[4] <=> (split(/\s+/,$b))[4]} <>' file
This is the shortest way to sort
and print the fifth column:
perl -E 'say for sort map{ (split)[4] } <>' file
Transforming the sort
Can we map
, split
and sort
in one pass? This is a short way to sort the fifth column:
perl -E 'say for sort map{ [(split)[4], $_]->[0] } <>' file
Dissecting this last example: perl
first maps
the STDIN
to split()
- making a list; takes the fifth element (i.e. [4]
) of this split()
list and wraps that list item and the whole line that was just read ($_
) inside an array constructor []
; then takes the first element of that anonymous array (i.e. the fifth column of each line) and passes it to sort()
. Phew!
This just prints the fifth column since we only passed the first element ->[0]
of the anonymous array to sort
. To print the whole line sorted by the column in this way we need to pass the whole anonymous array to sort
and tell sort
to use the element which holds the column's contents to do its work, and then pass the other element of the anonymous array (the one that holds the entire line) to print
(or say
) - this way we can sort by the fifth column, but print out the whole line:
perl -E 'say $_->[1] for sort{$a->[0] <=> $b->[0]} map{[(split)[4], $_]} <>' file
This is just like our very first example above. If, instead of running through the list that is created using for
, we map
the second element and pass it to print
we get:
perl -E 'say map $_->[1], sort{$a->[0] <=> $b->[0]} map{[(split)[4],$_]} <>' file
We have reinvented the Schwartzian transform which is such a great Perl idiom that it is "built in" to Perl 6 ;-)
To get a sense of how this works you can "visualize" things with Data::Printer:
perl -MDDP -e '@t = sort map{ [ (split /\s+/)[4], $_ ] } <> ; p @t' file
Learn more about Perl idioms from the Perl Idioms Explained posts at perlmonks.org and the Perl Beyond Syntax chapter of Modern Perl.
You might also like the nsort_by
function from List::UtilsBy
:
use List::UtilsBy 'rev_nsort_by';
my @sorted = rev_nsort_by { (split(' ', $_))[4] } @input;