2009-06-11 7 views
17

C'è un modo per ottenere un sub-hash? Devo usare una fetta di hash?Come posso ottenere una sola parte di un hash in Perl?

Ad esempio:

%hash = (a => 1, b => 2, c => 3); 

voglio solo

%hash = (a => 1, b => 2); 
+0

Poiché non c'è nulla logicamente "slice" su Sospetto che qualcosa abbia a che fare con 'map'. In un array lo si farebbe basandosi su un indice, ma in un hash non esiste una rappresentazione interna affidabile dei dati in esso contenuti. Avresti bisogno di un qualche tipo di qualificatore (regex?) Per definire cosa dovrebbe andare nel subhash e cosa non dovrebbe. –

+3

BTW, la sintassi dell'hash è sbagliata: usa le parentesi tonde "()" per un semplice hash, "{}" per i riferimenti hash anonimi. – trendels

risposta

46

Hash slices restituiscono i valori associati a un elenco di chiavi. Per ottenere una fetta hash si cambia il sigillo a @ e fornire un elenco di chiavi (in questo caso "a" e "b"):

my @items = @hash{"a", "b"}; 

spesso è possibile utilizzare un operatore citazione parola per produrre l'elenco:

my @items = @hash{qw/a b/}; 

è inoltre possibile assegnare ad una fetta di hash, quindi se volete un nuovo hash che contiene un sottoinsieme di un altro hash si può dire

my %new_hash; 
@new_hash{qw/a b/} = @hash{qw/a b/}; 

Molte persone utilizzano un map invece di fette di hash:

my %new_hash = map { $_ => $hash{$_} } qw/a b/; 

A partire da Perl 5.20.0, è possibile ottenere le chiavi ed i valori in un solo passaggio se si utilizza il sigillo% invece del sigillo @:

my %new_hash = %hash{qw/a b/}; 
+0

questo spiega perché "map" funziona con hash un po 'più chiaro. Grazie –

+0

Larry Wall, brian d foy, e ... Chas. Owens ... grazie per questa spiegazione molto completa! – bernie

+4

Probabilmente dovresti rendere più chiaro che una hash slice ha solo i valori, non le coppie chiave/valore. – ysth

-3

Un hash è un contenitore non ordinata, ma la fetta termine veramente solo ha senso in termini di un contenitore ordinato. Forse guarda usando un array. Altrimenti, potresti semplicemente rimuovere tutti gli elementi che non vuoi produrre il tuo 'sub-hash'.

+0

In Perl, le fette di hash sono utili e completamente supportate. Per saperne di più rileggere [la sezione 'Slices' in _perldata_] (http://perldoc.perl.org/perldata.html#Slices). –

9

che vorreste probabilmente per assemblare un elenco di chiavi che si desidera:

my @keys = qw(a b); 

e quindi utilizzare un ciclo per fare l'hash:

my %hash_slice; 
for(@keys) { 
    $hash_slice{$_} = %hash{$_}; 
} 

Oppure:

my %hash_slice = map { $_ => $hash{$_} } @keys; 

(La mia preferenza è la seconda, ma quella che preferisci è la migliore.)

3

Troppa programmazione funzionale mi porta a pensare prima a zip.

Con List::MoreUtils installato,

use List::MoreUtils qw(zip); 

%hash = qw(a 1 b 2 c 3); 
@keys = qw(a b); 
@values = @hash{@keys}; 
%hash = zip @keys, @values; 

Purtroppo, il prototipo di List :: di zip moreutils inibisce

zip @keys, @hash{@keys}; 

Se davvero si vuole evitare la variabile intermedia, si potrebbe

zip @keys, @{[@hash{@keys}]}; 

Oppure scrivi il tuo zip senza il prototipo problematico. (Questo non ha bisogno di List :: MoreUtils affatto.)

sub zip { 
    my $max = -1; 
    $max < $#$_and $max = $#$_ for @_; 
    map { my $ix = $_; map $_->[$ix], @_; } 0..$max; 
} 

%hash = zip \@keys, [@hash{@keys}]; 

Se avete intenzione di essere mutanti sul posto,

%hash = qw(a 1 b 2 c 3); 
%keep = map +($_ => 1), qw(a b); 
$keep{$a} or delete $hash{$a} while ($a, $b) = each %hash; 

evita la copia in più che le soluzioni map e zip incorrono. (. Sì, mutando l'hash mentre state iterando su di esso è sicuro ... fino a quando la mutazione è l'eliminazione solo la coppia più recente iterata)

+0

Spiega questo, per favore - '% keep = map + ($ _ => 1), qw (a b);'? –

3

FWIW, io uso Moose :: Autobox qui:

my $hash = { a => 1, b => 2, c => 3, d => 4 }; 
$hash->hslice([qw/a b/]) # { a => 1, b => 2 }; 

Nella vita reale, lo uso per estrarre "username" e "password" da un modulo di invio e passare a quello di Catalyst $c->authenticate (che si aspetta, nel mio caso, un hashref contenente nome utente e password, ma nient'altro).

+1

In che modo $ hash-> hslice (qw/a b /) è migliore di @ {$ hash} {qw/a b /}? O è uno di quei casi in cui, se devo chiedermi, sono troppo lontano per capire? –

+4

Dovresti avere un modello di risposta in modo che "I use Moose ::" sia all'inizio di ogni risposta. :) –

+1

Chas. Owens: non è quello che fa Hslice. @ {$ hash} {...} restituisce un elenco di valori; hslice restituisce un nuovo riferimento all'hash con entrambe le chiavi e i valori. – jrockway

6

Ancora un altro modo:

my @keys = qw(a b); 
my %hash = (a => 1, b => 2, c => 3); 
my %hash_copy; 
@hash_copy{@keys} = @hash{@keys}; 
2

Nuovo in Perl 5.20 è fette di hash restituzione delle chiavi e valori utilizzando% come sull'ultima riga qui:

my %population = ('Norway',5000000,'Sweden',9600000,'Denmark',5500000); 
my @slice_values = @population{'Norway','Sweden'}; # all perls can do this 
my %slice_hash = %population{'Norway','Sweden'}; # perl >= 5.20 can do this! 
Problemi correlati