2 Stimmen

Effiziente Möglichkeit zum Drucken von Hash von Hash von Arrays in Perl

Ich schreibe ein Skript, das den Inhalt eines Hashes eines Hashes von Arrays ausdruckt.

(Pseudocode):

my %hash = ();
$hash{key1}{key2} = ['value1', 'value2', 'value3', . . .];

oder

$hash{key1}{key2} = @array_of_values;

Grundsätzlich möchte ich dies für beliebige Kombinationen von Schlüsseln tun können und durch alle möglichen Schlüssel/Wert-Paare (oder wahrscheinlich genauer als Schlüssel, Schlüssel/Array-Paare ausgedrückt, da jeder Wert tatsächlich ein Array von Werten ist und jedes Array damit 2 Schlüssel hat) durchlaufen und die Ausgabe im folgenden Format ausdrucken:

"key1, key2, Wert1, Wert2, Wert3, . . .\n"

(Beispiel):

#!/usr/bin/perl

use strict;
use warnings;

# Hash initialisieren
my %hash = ();
my $string1 = "string1";
my $string2 = "string2";

# Strings in Arrays im Hash speichern
push @{$hash{a}{b}}, $string1;
push @{$hash{a}{b}}, $string2;
push @{$hash{c}{d}}, $string2;
push @{$hash{c}{d}}, $string1;

# Elemente des Hashes ausdrucken
# (möchte dies als Schleife für alle möglichen Schlüssel/Wert-Paare tun)
local $, = ',';
print "a, b, ";
print @{$hash{a}{b}};
print "\n";
print "c, d, ";
print @{$hash{c}{d}};
print "\n";

system ('pause');

Die Ausgabe dieses Codes wird unten angezeigt:

a, b, string1,string2
c, d, string2,string1
Drücken Sie eine beliebige Taste, um fortzufahren . . .

Ich dachte daran, den each-Operator zu verwenden, aber anscheinend funktioniert er nur für eindimensionale Hashes. (each gibt nur ein Schlüssel-Wert-Paar zurück, es funktioniert nicht korrekt, wenn 2 Schlüssel involviert sind)

Wie kann ich diesen Code optimieren, um durch den Hash in einer Schleife zu gehen und die gewünschte Ausgabe unabhängig von der Größe meines Hashes zu drucken?

6voto

TLP Punkte 65295

Die Verwendung von each funktioniert auch bei einem mehrstufigen Hash einwandfrei, man muss nur sicherstellen, dass das Argument ein Hash ist. Hier ist ein Beispiel, wie du es machen kannst. Ich habe auch gezeigt, wie ich diesen Hash initialisieren würde.

use strict;
use warnings;
use v5.14;

my $string1 = "string1";
my $string2 = "string2";
my %hash = (
    a => { 
        b => [ $string1, $string2 ],
    },
    c => {
        d => [ $string2, $string1 ],
    }
);

for my $key (keys %hash) {
    while (my ($k, $v) = each %{ $hash{$key} }) {
        say join ", ", $key, $k, @$v;
    }
}

Ausgabe:

c, d, string2, string1
a, b, string1, string2

Beachte die Einfachheit der Verwendung von @$v, um auf dein innerstes Array zuzugreifen, anstatt auf die etwas umständlichere Alternative @{ $hash{$key}{$k} }.

4voto

Borodin Punkte 124906

Dies ist am ordentlichsten mit einer rekursiven Unterfunktion, und da sie nur wenige Male rekursiv aufgerufen wird, ist es keine verschwenderische Lösung.

use strict;
use warnings;

my %hash;
my ($string1, $string2) = qw/ string1 string2 /;

push @{$hash{a}{b}}, $string1;
push @{$hash{a}{b}}, $string2;
push @{$hash{c}{d}}, $string2;
push @{$hash{c}{d}}, $string1;

print_data(\%hash);

sub print_data {
    my ($ref, $prefix) = (@_, '');

   if (ref $ref eq 'ARRAY') {
      print $prefix, join(', ', @$ref), "\n";
   }
   else {
      print_data($ref->{$_}, "$prefix$_, ") for sort keys %$ref;
   }
}

Ausgabe

a, b, string1, string2
c, d, string2, string1

3voto

Michael Carman Punkte 30300

Sie haben nicht angegeben, warum Sie den Inhalt Ihres HoHoA (Hash von Hashes von Arrays) drucken möchten. Wenn es für Debugging-Zwecke ist, würde ich entweder Data::Dumper (Core) oder Data::Dump (auf CPAN) verwenden.

use Data::Dumper qw(Dumper);
print Dumper \%hash;

oder

use Data::Dump qw(pp);
pp \%hash;

Unter der Annahme, dass es einen Grund gibt, warum Sie die Ausgabe so formatiert haben möchten wie in Ihrem Beispiel (und dass es immer und nur ein HoHoA ist), würde ich eine verschachtelte Schleife verwenden:

while (my ($ok, $ov) = each %hash) {
    while (my ($ik, $iv) = each %$ov) {
        say join ',', @$iv;
    }
}

Ich empfehle nicht, map zu verwenden. Es eignet sich am besten zur Listen-Transformation und nicht zur Flusskontrolle, und es ist umständlich, die äußeren und inneren Schlüssel mit verschachtelten map-Blöcken zu verfolgen, die beide $_ für verschiedene Dinge verwenden. Da Sie und G. Cito Interesse daran geäußert haben, eine zu sehen, hier ist sie trotzdem:

say foreach map {
    my $o = $_;
    map {
        my $i = $_;
        join ',', $o, $i, @{$hash{$o}{$i}}
    } keys %{$hash{$o}};
} keys %hash;

2voto

i alarmed alien Punkte 9212

Um einen Hash von Arrays zu drucken, können Sie die Datenstruktur mit foreach oder for my durchlaufen:

# Schlüssel des äußeren Hash
foreach my $oh (keys %hash) {
    # Schlüssel des inneren Hash
    foreach my $ih (keys %{$hash{$oh}}) {
        # $hash{$oh}{$ih} ist das Array, also kann es so gedruckt werden:
        print join(", ", $oh, $ih, @{$hash{$oh}{$ih}}) . "\n";

        # oder Sie können die Elemente so durchlaufen:
        # foreach my $arr (@{$hash{$oh}{$ih}})
        # {   doSomethingTo($arr); }

    }
}

Und für alle Kartografie-Fans, hier ist eine map Version:

map { my $oh = $_; 
    map { say join( ", ", $oh, $_, @{$hash{$oh}{$_}} )  } keys %{$hash{$_}}
} keys %hash;

1voto

tjwrona1992 Punkte 8091

Ich habe darüber nachgedacht und eine mögliche Lösung gefunden. Wenn jemand eine elegantere Lösung hat, würde ich sie gerne sehen. Danke!

Hier ist die Lösung, die ich gefunden habe:

#!/usr/bin/perl

use strict;
use warnings;

# initialize hash
my %hash = ();
my $string1 = "string1";
my $string2 = "string2";

# push strings onto arrays stored in hash
push @{$hash{a}{b}}, $string1;
push @{$hash{a}{b}}, $string2;
push @{$hash{c}{d}}, $string2;
push @{$hash{c}{d}}, $string1;

# prints hash of hash of arrays in a loop :)
my @keys1 = keys %hash;
for my $key1 (@keys1) {
    my @keys2 = keys $hash{$key1};
    for my $key2 (@keys2) {
        local $" = ', ';
        print "$key1, $key2, @{$hash{$key1}{$key2}}";
        print "\n";
    }
}

system ('pause');

Diese Lösung kümmert sich jedoch nicht um die Reihenfolge der Schlüssel, sodass sie in einer zufälligen Reihenfolge gedruckt wird.

Ausgabe:

c, d, string2, string1
a, b, string1, string2
Drücken Sie eine beliebige Taste, um fortzufahren . . .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X