2012-05-20 16 views
5

Sto cercando un modulo logico (non aggiuntivo) da ordinare in base a tale formato. Ho una lista di stringhe che si presenta come:ordinamento per mmyy (mese e anno)

asdadasBBBsfasdasdas-0112 
asdanfnfnfnfnf222ads-1210 

ecc I cant appena sorta dai numeri, perché, ad esempio: 812> 113 (812 = Agosto 2012, 113 = gennaio 2013, quindi la sua non corretta)

qualsiasi buona strategia ??

grazie,

risposta

5

Un Trasformata di Schwartz sarebbe uno spreco enorme qui. Questo simile costrutto il cui nome non potrò mai ricordare sarebbe molto meglio.

my @sorted = 
    map substr($_, 4), 
    sort 
    map substr($_, -2) . substr($_, -4, 2) . $_, 
     @unsorted; 

Utilizzando l'operatore partita invece di substr:

my @sorted = 
    map substr($_, 4), 
    sort 
    map { /(..)(..)\z/s; $2.$1.$_ } 
     @unsorted; 
+2

[Guttman Rosler Transform] (http://www.perlmonks.org/?node_id=145659) –

+1

Per i benchmark che ho eseguito su dimensioni di array da 10 a 200k, GRT era 2-5x più veloce di ST o implementazioni ingenue. –

+0

Benchmark [performance] (http://i.imgur.com/LSFtm.png), [code] (https://gist.github.com/2764370) –

2

utilizza una funzione di ordinamento che guarda l'anno prima, e poi la data:

sub mmyy_sorter { 

    my $a_yy = substr($a, -2); 
    my $b_yy = substr($b, -2); 

    my $a_mm = substr($a, -4, 2); 
    my $b_mm = substr($b, -4, 2); 

    return ($a_yy cmp $b_yy) || ($a_mm cmp $b_mm); 
} 

my @sorted = sort mmyy_sorter @myarray; 

NB: questo è tecnicamente non così efficace come potrebbe essere in quanto ha per ricalcolare i sottocampi del mese e dell'anno per ogni confronto, non solo una volta per ciascun elemento nell'array.

Sarebbe anche possibile sfruttare la conversione di tipo automatico di Perl e utilizzare l'operatore <=> al posto di cmp, poiché tutti i valori rappresentano effettivamente numeri.

+0

sottostringhe Informatica è a buon mercato. Hai un benchmark contro una trasformazione Schwartziana piena? –

+0

@GregBacon Non l'ho, ma non mi aspetterei alcuna differenza sostanziale nelle prestazioni fino a raggiungere 1000+ voci. – Alnitak

+0

Ho corretto l'errore di battitura che ha impedito la corretta routine di ordinamento. – pilcrow

0

Che ne dici di rifarlo a mesi? Per esempio:

812 = 12 * 12 + 8

113 = 13 * 12 + 1

È possibile trasformare anni in mesi e sarà un bene. Per selezionare i numeri è possibile utilizzare regex.

5

Come su Schwartzian transform:

#!/usr/bin/perl 
use strict; 
use warnings; 
use Data::Dump qw(dump); 

my @list = (
    'asdadasBBBsfasdasdas-0112', 
    'asdanfnfnfnfnf222ads-1210', 
    'asdanfnfnfnfnf222ads-1211', 
    'asdanfnfnfnfnf222ads-1010', 
    'asdanfnfnfnfnf222ads-1011', 
); 

my @sorted = 
    map { $_->[0] } 
    sort { $a->[1] <=> $b->[1] or $a->[2] <=> $b->[2] } 
    map { /-(\d\d)(\d\d)$/; [$_, $2, $1] } @list; 
dump @sorted; 

uscita:

(
    "asdanfnfnfnfnf222ads-1010", 
    "asdanfnfnfnfnf222ads-1210", 
    "asdanfnfnfnfnf222ads-1011", 
    "asdanfnfnfnfnf222ads-1211", 
    "asdadasBBBsfasdasdas-0112", 
) 
+1

+1 per il modo perlish usando una trasformazione di Schwartzian. Io stesso avrei usato '$ _-> [2]' invece di 'splice' tho. – dgw

+1

Ho sempre messo la stringa in '$ _-> [0]' quando uso ST. In questo modo, la mappa in alto è sempre 'map $ _-> [0],'. In questo caso, la parte inferiore sarebbe semplificata per 'mappare [$ _, /(..)(..)\z/s],'. – ikegami

0

Grazie a @ M42 per i dati di esempio.

use strict; 
use warnings; 
use feature 'say'; 

my @list = (
    'asdadasBBBsfasdasdas-0112', 
    'asdanfnfnfnfnf222ads-1210', 
    'asdanfnfnfnfnf222ads-1211', 
    'asdanfnfnfnfnf222ads-1010', 
    'asdanfnfnfnfnf222ads-1011', 
); 

my @sorted = sort { 
    my ($aa, $bb) = map { /(..)(..)\z/ and $2.$1 } $a, $b; 
    $aa <=> $bb; 
} @list; 

say for @sorted; 

uscita

asdanfnfnfnfnf222ads-1010 
asdanfnfnfnfnf222ads-1210 
asdanfnfnfnfnf222ads-1011 
asdanfnfnfnfnf222ads-1211 
asdadasBBBsfasdasdas-0112 
Problemi correlati