2016-07-26 9 views
0

Ich versuche Array von Hash-Referenz zu erzeugen, erstellt durch Zusammenfügen aller Schlüssel von Hashes mit Sortierung. Betrachten I dynamische Hashreferenz haben wieErzeuge Array von Referenz-Hash

my $hash_ref = { 
      'A1' => { 
        'B2' => { 
          'C1' => { 
            'D1' => {}, 
            'D2' => {}, 
            'D3' => {} 
           } 
         }, 
        'B3' => { 
          'C1' => { 
            'D2' => {}, 
            'D1' => {}, 
            'D3' => {} 
           } 
         }, 
        'B1' => { 
          'C1' => { 
            'D1' => {}, 
            'D2' => {} 
           } 
         } 
       } 
     }; 

wie Array aus wie

@arr = qw/A1B1C1D1 A1B1C1D2 A1B2C1D1 ..../;

unten oben Hash zu erzeugen, ist der Code I versucht (die nicht funktioniert)

my $out = hash_walk($hash_ref); 

say Dumper $out; 

sub hash_walk { 
    my $hash = shift; 
    my $array_ref; 
    my $temp_arr; 
    my @temp_arr2; 
    foreach my $k (sort keys %$hash) { 
     $v = $$hash{$k}; 

     if (ref($v) eq 'HASH') { 

      # Recurse. 
      $temp_arr = hash_walk($v); 

     } 
     push @$array_ref, $k if $k; 

     my (@lvlfirst, @lvlnext); 

     if ($array_ref && $temp_arr){ 
      @lvlfirst = @$array_ref; 
      @lvlnext = @$temp_arr; 
     } 

     for (my $i = 0 ; $i <= $#lvlfirst ; $i++) { 
      for (my $j = 0 ; $j <= $#lvlnext ; $j++) { 
       push @temp_arr2, "$lvlfirst[$i]$lvlnext[$j]"; ##Trying to join here 

      } 
     } 
    } 

    return \@temp_arr2; 
} 

XML ist:

<root> 
    <class1 name="A1"> 
    <class2 name="B1"> 
     <class3 name="C1"> 
     <class4 name="D1"></class4> 
     <class4 name="D2"></class4> 
     </class3> 
    </class2> 
    <class2 name="B2"> 
     <class3 name="C1"> 
     <class4 name="D1"></class4> 
     </class3> 
    </class2> 
    <class2 name="B3"> 
     <class3 name="C1"> 
     <class4 name="D1"></class4> 
     <class4 name="D2"></class4> 
     <class4 name="D3"></class4> 
     </class3> 
    </class2> 
    </class1> 
</root> 
+1

Das klingt wie ein XY-Problem. Könnten Sie das etwas näher erläutern? Normalerweise rekursive Strukturen sind aus einem Grund rekursiv. – Sobrique

+0

@Sobrique Die Eingabehashref wird aus einer Klassifizierungshierarchie von Daten in XML generiert. Ich muss Daten verbinden, um Zeichenketten zu erzeugen, die Elternkindunterseite darstellen. – waghso

+1

Ok. Kann ich vorschlagen, einen Schritt zurück zu machen? Verwenden Sie einen XML-Parser - posten Sie Ihren XML-Code, und wir können Ihnen leichter geben, was Sie wollen, wenn XML-Parser diese Rekursion implizit verarbeiten. – Sobrique

Antwort

3

Sie sollten sich wirklich etwas anstrengen, bevor Sie sich an SO wenden, um Hilfe zu erhalten. Es ist viel wahrscheinlicher, dass wir Ihnen helfen, kaputten Code zu reparieren, als Ihnen nur eine Antwort zu geben.

Aber ich fühle mich großzügig und ich habe ein paar Minuten zu sparen.

Der Brute-Force-Ansatz wäre, jeden Schlüssel auf jeder Ebene im Hash zu durchlaufen.

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

use Data::Dumper; 

my $hash_ref = { 
    'A1' => { 
     'B2' => { 
      'C1' => { 
       'D1' => {}, 
       'D2' => {}, 
       'D3' => {} 
      } 
     }, 
     'B3' => { 
      'C1' => { 
       'D2' => {}, 
       'D1' => {}, 
       'D3' => {} 
      } 
     }, 
     'B1' => { 
      'C1' => { 
       'D1' => {}, 
       'D2' => {} 
      } 
     } 
    } 
}; 

my @arr; 

for my $l1 (sort keys %$hash_ref) { 
    for my $l2 (sort keys %{$hash_ref->{$l1}}) { 
    for my $l3 (sort keys %{$hash_ref->{$l1}{$l2}}) { 
     for my $l4 (sort keys %{$hash_ref->{$l1}{$l2}{$l3}}) { 
     push @arr, "$l1$l2$l3$l4"; 
     } 
    } 
    } 
} 

say Dumper \@arr; 

Dies erzeugt die Ausgabe:

$VAR1 = [ 
      'A1B1C1D1', 
      'A1B1C1D2', 
      'A1B2C1D1', 
      'A1B2C1D2', 
      'A1B2C1D3', 
      'A1B3C1D1', 
      'A1B3C1D2', 
      'A1B3C1D3' 
     ]; 

Update: Hier ist eine rekursive Lösung:

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

use Data::Dumper; 

my $hash_ref = { 
    'A1' => { 
     'B2' => { 
      'C1' => { 
       'D1' => {}, 
       'D2' => {}, 
       'D3' => {} 
      } 
     }, 
     'B3' => { 
      'C1' => { 
       'D2' => {}, 
       'D1' => {}, 
       'D3' => {} 
      } 
     }, 
     'B1' => { 
      'C1' => { 
       'D1' => {}, 
       'D2' => {} 
      } 
     } 
    } 
}; 

my @arr = walk_hash($hash_ref, ''); 

say Dumper \@arr; 

sub walk_hash { 
    my ($hash_ref, $prefix) = @_; 

    return $prefix unless keys %$hash_ref; 
    return map { walk_hash($hash_ref->{$_}, "$prefix$_") } sort keys %$hash_ref; 
} 
+0

hinzugefügt den Code, den ich versuche – waghso

+0

Danke für die Antwort Dave. Dieser Code schlägt jedoch fehl, wenn nur zwei Verschachtelungsebenen vorhanden sind. Der Hash, den ich versuche, hat eine dynamische Verschachtelung, und es wird zu kompliziert, wenn ich versuche, es weiter zu kodieren. – waghso

+0

Ja, eine dynamische Anzahl von Hash-Werten bedeutet, dass Sie eine rekursive Lösung benötigen. Ich habe vielleicht Zeit, mich das etwas später anzusehen. –

2

ich das anders angehen würde - wie diese XML ist, würde ich überspringen die intermediate "zerlegen Sie den XML-Code in einen Hash-Schritt" und arbeiten Sie direkt mit ihm.

So etwas tut, was Sie wollen:

#!/usr/bin/env perl 
use strict; 
use warnings 'all'; 

use XML::Twig; 
use Data::Dumper; 

my $twig = XML::Twig -> new -> parsefile ('your.xml'); 

my @node_keys; 

#find all the nodes with a name attribute. 
#then grep out the ones that have child nodes. 
foreach my $elt (grep { not $_ -> descendants } $twig -> get_xpath('//*[@name]')){ 
    my $path = $elt -> att('name'); 
    my $cursor = $elt; 
    #recurse upwards through 'parent' nodes with a 'name' attribute. 
    while ($cursor -> parent -> att('name')) { 
     $path = $cursor -> parent -> att('name') . $path; 
     $cursor = $cursor -> parent; 
    } 
    push @node_keys, $path; 
} 

print Dumper \@node_keys; 

Gibt Ausgang:

$VAR1 = [ 
      'A1B1C1D1', 
      'A1B1C1D2', 
      'A1B2C1D1', 
      'A1B3C1D1', 
      'A1B3C1D2', 
      'A1B3C1D3' 
     ]; 

Hinweis - es ist die Erhaltung der gleichen Reihenfolge wie Quelle, weil es in 'XML Ordnung' ist zu Fuß. Das könnte eine Funktion genannt werden, oder Sie können es später sortieren.

Aber ich würde vielleicht fragen, was Sie erreichen möchten, indem Sie diese Verbindungen von 'Name' Attribute machen - es kann sein, dass Sie die Aufgabe effizienter durch XML-Parsing und xpath Abfragen lösen können.