2010-08-26 37 views
5

Angenommen, ich habe einen Hash wie folgt aus:Wie kann man herausfinden, ob ein Perl-Hash multidimensional ist?

$hash_ref = { 

    'hashes' => { 
    'h1' => { 'klf' => '1', 'moomooland' => '1' }, 
    'h2' => { 'klf' => '2', 'moomooland' => '2' }, 
    'h3' => { 'klf' => '3', 'moomooland' => '3' } 
    }, 

    'keys' => { 
    'k1' => 'key1', 
    'k2' => 'key2', 
    'k3' => 'key3' 
    } 

} 

Wie konnte ich herausfinden, so einfach wie möglich, dass hashes enthält 3 weitere Hashes, während keys enthält drei Schlüssel/Wert-Paare?

ref wird HASH für beide zurückgeben, und ich bin mir nicht sicher, ob es möglich ist, vielleicht die Tiefe dieser Hashes herauszufinden.

Thanks :)

+0

Ja, es ist möglich, aber ich frage mich, warum Sie das wissen möchten. Das kann helfen, eine geeignete Vorgehensweise zu finden. z.B. Möchten Sie die tiefste Kette oder die Tiefe aller verschiedenen Zweige zurückgeben? – Zaid

+2

Mögliches Duplikat: [Traversieren eines mehrdimensionalen Hash in Perl] (http://stackoverflow.com/questions/160175/traversing-a-multi-dimensional-hash-in-perl) – Zaid

+0

Ich müsste das so wissen Ich kann verschiedene Aktionen aufrufen, je nachdem, ob ich 3 Hashes oder 3 Schlüssel/Wert-Paare erhalte. Also muss ich nicht unbedingt die genaue Tiefe kennen, wenn ich diese beiden auf andere Weise unterscheiden kann. Ich sehe jetzt deinen Link zu einer anderen Frage, aber ich hoffte, dass dies ohne Rekursion gemacht werden könnte :) – sentinel

Antwort

5

Wenn Sie nur wissen, ob ein Hash mehrdimensionale ist, können Sie die Werte iterieren und stoppen, wenn eine Referenz gefunden wird:

my $is_multi_dimensional = 0; 

for my $value (values %$hash_ref) { 
    if ('HASH' eq ref $value) { 
     $is_multi_dimensional = 1; 
     last; 
    } 
} 

oder Sie verwenden können, die each() Funktion:

while (my (undef, $value) = each %$hash_ref) { 
    if ('HASH' eq ref $value) { 
     $is_multi_dimensional = 1; 
     last; 
    } 
} 
+0

Oh, das ist ziemlich brillant. So einfach, doch so logisch und effektiv. Argh, warum habe ich nicht daran gedacht? :) Danke Eugene, das funktioniert ganz gut für das, was ich brauche, obwohl ich jetzt merke, dass der Wortlaut meiner Frage nicht wirklich erklärt, dass dies genau das ist, wonach ich gesucht habe, anstatt genaue/tatsächliche Tiefe eines Hashes zu erhalten. Hoppla. – sentinel

+1

Sind Sie sicher, dass jedes() effizienter ist als die Werte()? Da es den internen Iterator verwendet, kann es einen Kurzschluss verursachen, was für riesige Hashes hilfreich ist, aber es hat den allgemeinen Nachteil, den (globalen!) Hash-Iterator zu verwenden. each() ist ein fehlerhaftes Merkmal, IMO. – tsee

+2

@tsee: es ist effizienter für größere Hashes, weil es keine Zwischenliste der Werte erstellt. Und es ist der einzige Weg für sehr große (z. B. gebundene) Hashes. Natürlich sollte man sich daran erinnern, dass es für jeden Hash im Programm einen einzigen Iterator gibt. –

1

diesen kann man versuchen, die Tiefe von Perl Hash wissen:

#!/usr/bin/perl 
use strict; 
my $hash_ref = { 

    'hashes' => { 
    'h1' => { 'klf' => '1', 'moomooland' => '1' }, 
    'h2' => { 'klf' => '2', 'moomooland' => '2' }, 
    'h3' => { 'klf' => '3', 'moomooland' => '3' } 
    }, 

    'keys' => { 
    'k1' => 'key1', 
    'k2' => 'key2', 
    'k3' => 'key3' 
    } 

}; 
print_nested_hash($hash_ref, 0); 

sub print_nested_hash { 
    my $hash_ref = shift; 
    my $depth = shift; 
    foreach my $key (sort keys %{$hash_ref}) { 
     print ' ' x $depth, $key, "\n"; 

     if (ref($hash_ref->{$key}) eq "HASH") { 
      print_nested_hash($hash_ref->{$key}, $depth+1); 
     } 
    } 
} 

OUTPUT:

hashes 
    h1 
     klf 
     moomooland 
    h2 
     klf 
     moomooland 
    h3 
     klf 
     moomooland 
keys 
    k1 
    k2 
    k3 
5

Sie können auch first und grep dafür:

use List::Util 'first'; 
say 'is multi-dimensional' if first { ref } values %$hash_ref; 

# explicitly check for HASH ref this time 
my $how_many_md_hashes = grep { 'HASH' eq ref } values %$hash_ref; 

NB. first (Teil von List::Util Kern-Modul) Kurzschluss ist ideal für Conditionals und ist wahrscheinlich die schnellste aller möglichen Optionen.

/I3az/

+2

Sie können "any" anstelle von "first" verwenden, wenn Sie sich nur für die Wahrheit interessieren und nicht für den tatsächlichen Wert, der zutrifft: "use List :: MoreUtils 'any"; my $ is_multi_dimensional = beliebige {ref} Werte% $ hash_ref; ' – Ether

Verwandte Themen