Usando Perl, come posso ordinare un array usando il valore di un numero all'interno di ogni elemento dell'array?

StackOverflow https://stackoverflow.com/questions/809804

  •  03-07-2019
  •  | 
  •  

Domanda

Diciamo che ho un array, @theArr, che contiene circa 1.000 elementi come il seguente:

01  '12 16 sj.1012804p1012831.93.gz'
02  '12 16 sj.1012832p1012859.94.gz'
03  '12 16 sj.1012860p1012887.95.gz'
04  '12 16 sj.1012888p1012915.96.gz'
05  '12 16 sj.1012916p1012943.97.gz'
06  '12 16 sj.875352p875407.01.gz'
07  '12 16 sj.875408p875435.02.gz'
08  '12 16 sj.875436p875535.03.gz'
09  '12 16 sj.875536p875575.04.gz'
10  '12 16 sj.875576p875603.05.gz'
11  '12 16 sj.875604p875631.06.gz'
12  '12 16 sj.875632p875659.07.gz'
13  '12 16 sj.875660p875687.08.gz'
14  '12 16 sj.875688p875715.09.gz'
15  '12 16 sj.875716p875743.10.gz'
...

Se la mia prima serie di numeri (tra 'sj.' e 'p') fosse sempre di 6 cifre, non avrei problemi. Tuttavia, quando i numeri si sovrappongono a 7 cifre, l'ordinamento predefinito smette di funzionare poiché i numeri di 7 cifre più grandi vengono prima del numero di 6 cifre più piccolo.

C'è un modo per dire a Perl di ordinare in base a quel numero all'interno della stringa in ciascun elemento dell'array?

È stato utile?

Soluzione

Sembra che tu abbia bisogno di una Schwartzian Transform :

#!/usr/bin/perl

use strict;
use warnings;

my @a = <DATA>;

print 
    map  { 

Sembra che tu abbia bisogno di una Schwartzian Transform :

<*>->[1] } #get the original value back sort { $a->[0] <=> $b->[0] } #sort arrayrefs numerically on the sort value map { /sj\.(.*?)p/; [$1,

Sembra che tu abbia bisogno di una Schwartzian Transform :

<*>] } #build arrayref of the sort value and orig @a; __DATA__ 12 16 sj.1012804p1012831.93.gz 12 16 sj.1012832p1012859.94.gz 12 16 sj.1012860p1012887.95.gz 12 16 sj.1012888p1012915.96.gz 12 16 sj.1012916p1012943.97.gz 12 16 sj.875352p875407.01.gz 12 16 sj.875408p875435.02.gz 12 16 sj.875436p875535.03.gz 12 16 sj.875536p875575.04.gz 12 16 sj.875576p875603.05.gz 12 16 sj.875604p875631.06.gz 12 16 sj.875632p875659.07.gz 12 16 sj.875660p875687.08.gz 12 16 sj.875688p875715.09.gz 12 16 sj.875716p875743.10.gz

Altri suggerimenti

Puoi usare una regex per estrarre il numero da ogni riga all'interno del blocco che passi alla funzione di ordinamento:

@newArray = sort { my ($anum,$bnum); $a =~ /sj\.([0-9]+)p/; $anum = $1; $b =~ /sj\.(\d+)p/; $bnum = $1; $anum <=> $bnum } @theArr;

Tuttavia, Chas. La soluzione di Owens è migliore, poiché fa corrispondere la regex solo una volta per ogni elemento.

Ecco un esempio che li ordina in ordine crescente, supponendo che non ti interessi troppo all'efficienza:

use strict;

my @theArr = split(/\n/, <<END_SAMPLE);
12 16 sj.1012804p1012831.93.gz
12 16 sj.1012832p1012859.94.gz
12 16 sj.1012860p1012887.95.gz
12 16 sj.1012888p1012915.96.gz
12 16 sj.1012916p1012943.97.gz
12 16 sj.875352p875407.01.gz
12 16 sj.875408p875435.02.gz
12 16 sj.875436p875535.03.gz
12 16 sj.875536p875575.04.gz
12 16 sj.875576p875603.05.gz
END_SAMPLE

my @sortedArr = sort compareBySJ @theArr;

print "Before:\n".join("\n", @theArr)."\n";
print "After:\n".join("\n", @sortedArr)."\n";

sub compareBySJ {
    # Capture the values to compare, against the expected format
    # NOTE: This could be inefficient for large, unsorted arrays
    #       since you'll be matching the same strings repeatedly
    my ($aVal) = $a =~ /^\d+\s+\d+\s+sj\.(\d+)p/
        or die "Couldn't match against value $a";
    my ($bVal) = $b =~ /^\d+\s+\d+\s+sj\.(\d+)p/
        or die "Couldn't match against value $a";

    # Return the numerical comparison of the values (ascending order)
    return $aVal <=> $bVal;
}

Uscite:

Before:
12 16 sj.1012804p1012831.93.gz
12 16 sj.1012832p1012859.94.gz
12 16 sj.1012860p1012887.95.gz
12 16 sj.1012888p1012915.96.gz
12 16 sj.1012916p1012943.97.gz
12 16 sj.875352p875407.01.gz
12 16 sj.875408p875435.02.gz
12 16 sj.875436p875535.03.gz
12 16 sj.875536p875575.04.gz
12 16 sj.875576p875603.05.gz
After:
12 16 sj.875352p875407.01.gz
12 16 sj.875408p875435.02.gz
12 16 sj.875436p875535.03.gz
12 16 sj.875536p875575.04.gz
12 16 sj.875576p875603.05.gz
12 16 sj.1012804p1012831.93.gz
12 16 sj.1012832p1012859.94.gz
12 16 sj.1012860p1012887.95.gz
12 16 sj.1012888p1012915.96.gz
12 16 sj.1012916p1012943.97.gz

Sì. La funzione sort utilizza una funzione di confronto opzionale che verrà utilizzata per confrontare due elementi. Può assumere la forma di un blocco di codice o del nome di una funzione da chiamare.

C'è un esempio nel documento collegato che è simile a quello che vuoi fare:

# inefficiently sort by descending numeric compare using
# the first integer after the first = sign, or the
# whole record case-insensitively otherwise

@new = sort {
($b =~ /=(\d+)/)[0] <=> ($a =~ /=(\d+)/)[0]
            ||
            uc($a)  cmp  uc($b)
} @old;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top