2009-06-11 11 views
17

Gibt es eine Möglichkeit, einen Sub-Hash zu erhalten? Muss ich ein Hash-Slice verwenden?Wie kann ich einen einzigen Teil eines Hash in Perl bekommen?

Zum Beispiel:

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

Ich mag nur

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

Da es nichts ist logisch "Slice" auf Ich vermute etwas mit 'map' zu tun. In einem Array würden Sie es basierend auf einem Index tun, aber in einem Hash gibt es keine zuverlässige interne Darstellung der Daten darin. Sie benötigen einen Typ von Qualifier (Regex?), Um zu definieren, was in den Subhash gehen soll und was nicht. –

+3

BTW, Sie Hash-Syntax ist falsch: Verwenden Sie runde Klammern "()" für einen einfachen Hash, "{}" für anonyme Hash-Referenzen. – trendels

Antwort

46

Hash slices geben Sie die Werte zurück, die mit einer Liste von Schlüsseln verknüpft sind. Um ein Hash-Slice Sie das Siegel zu @ ändern und stellt eine Liste von Schlüsseln (in diesem Fall "a" und "b"):

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

Oft können Sie ein Zitat Wort Operator verwenden, um die Liste zu produzieren:

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

Sie können auch auf einen Hash Scheibe, weisen also, wenn Sie einen neuen Hash möchten, die eine Teilmenge einer anderen Hash enthält können Sie

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

Viele Menschen eine 012 sagen verwendenstatt Hash-Scheiben:

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

Beginnend mit Perl 5.20.0, können Sie die Schlüssel und die Werte in einem Schritt, wenn man das% Sigill anstelle des @ Sigill verwenden:

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

das macht warum "map" funktioniert w/hashes ein wenig klarer zu mir. Danke –

+0

Larry Wall, brian d foy, und ... Chas. Owens ... danke für diese sehr ausführliche Erklärung! – bernie

+4

Sie sollten wahrscheinlich deutlicher machen, dass ein Hash-Slice nur die Werte hat, nicht Schlüssel/Wert-Paare. – ysth

-3

Ein Hash ein ungeordneter Behälter ist, aber der Begriff Scheibe nur dann wirklich sinnvoll im Sinne eines geordneten Behälters. Vielleicht schauen Sie sich ein Array an. Andernfalls müssen Sie möglicherweise nur alle Elemente entfernen, die nicht Ihren 'Sub-Hash' erzeugen sollen.

+0

In Perl sind Hash-Slices nützlich und werden vollständig unterstützt. Um mehr darüber zu erfahren, lesen Sie [der Abschnitt 'Slices' in _perldata_] (http://perldoc.perl.org/perldata.html#Slices). –

9

Sie würden vermutlich eine Liste der Schlüssel montieren wollen Sie wollen:

my @keys = qw(a b); 

Und dann eine Schleife verwenden, um den Hash zu machen:

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

Oder:

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

(Meine Vorliebe ist die zweite, aber die, die Sie mögen, ist am besten.)

3

Zu viel funktionale Programmierung führt mich zuerst an zip denken.

Mit List::MoreUtils installiert,

use List::MoreUtils qw(zip); 

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

Leider ist der Prototyp der Liste :: MoreUtils der zip hemmt

zip @keys, @hash{@keys}; 

Wenn Sie wirklich die Zwischenvariable vermeiden wollen, könnten Sie

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

Oder schreiben Sie einfach Ihre eigene zip ohne den problematischen Prototyp. (Dies benötigt List :: MoreUtils überhaupt nicht.)

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

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

Wenn Sie an Ort und Stelle zu mutieren,

%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; 

vermeidet das zusätzliche Kopieren gehen, dass die map und zip Lösungen entstehen. (. Ja, Mutieren der Hash, während Sie über es sind Iterieren ist sicher ... solange die Mutation nur die zuletzt iteriert Paar zu löschen)

+0

Erklären Sie das bitte - '% keep = map + ($ _ => 1), qw (a b);'? –

3

FWIW, ich benutze Moose :: Autobox hier:

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

Im echten Leben, ich benutze dies um "Benutzername" und "Passwort" aus einem Formular zu extrahieren, und übergeben diese an Catalyst $c->authenticate (die in meinem Fall erwartet, eine Hashref mit dem Benutzernamen und Passwort, aber sonst nichts).

+1

Wie ist $ hash-> hslice (qw/a b /) besser als @ {$ hash} {qw/a b /}? Oder ist das einer jener Fälle, in denen, wenn ich fragen muss, ich zu weit gegangen bin, um zu verstehen? –

+4

Sie sollten eine Antwortvorlage haben, so dass "Ich verwende Moose ::" am Anfang jeder Antwort steht. :) –

+1

Chas. Owens: Das ist nicht was Hslice tut. @ {$ hash} {...} gibt eine Liste von Werten zurück; hslice gibt eine neue Hash-Referenz mit Schlüssel und Werten zurück. – jrockway

6

Noch eine andere Art und Weise:

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

Neu in Perl 5.20 ist Hash-Scheiben Schlüssel sowie Werte durch die Verwendung% wie in der letzten Zeile hier Rückkehr:

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! 
Verwandte Themen