How can I sort an array or table by column in Perl?

前端 未结 3 2049
孤独总比滥情好
孤独总比滥情好 2020-12-18 06:10

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

相关标签:
3条回答
  • 2020-12-18 06:14

    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.

    0 讨论(0)
  • 2020-12-18 06:34

    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.

    0 讨论(0)
  • 2020-12-18 06:37

    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;
    
    0 讨论(0)
提交回复
热议问题