[Perl] 2D-Hash


kickerxy123

Erfahrenes Mitglied
#1
Hallo zusammen,

ich habe ein Problem mit Perl. Da ich mit der Skriptsprache erst seit einem Tag arbeite, bin ich noch nicht ganz sicher im Umgang mit Perl...

Hier zunächst der relevante Code:
Code:
my @rows = getRows(...........); #@rows ist ein 2D-Array!
my %aggSize = undef;

foreach my $row(@rows)	#row is an array, since rows is a 2d array therefore access is @$row and not $row 
{
	if(exists $aggSize{@$row[1]})
	{
		$aggSize{@$row[1]}->{size} = "".(int($aggSize{@$row[1]}->{size}) + int(@$row[-1]));
		$aggSize{@$row[1]}->{name} = $aggSize{@$row[1]}->{name} . ",\"" . @$row[-2] . "\"";
	}
	else #neuer key, hash ergänzen
	{
		%aggSize = (%aggSize, @$row[1] => { size => @$row[-1], name => "\"".@$row[-2]."\""}); #hash of hash
	}	 
}

 

foreach my $ip(%aggSize) #ip is a hash since aggSize is a 2d hash therfore access with %$ip rather than $ip
{
	printf "IP: " . $ip . "\tSize:" . formatBytes($aggSize{%$ip->{size}}) . "\tFiles: " . $aggSize{%$ip->{name}} . "\n";
}

Leider ist der Output nicht so ganz das, was ich erwartet habe.
Output (IPs verändert):
Code:
IP: 20.20.20.21	Size:149.53 MBytes	Files: HASH(0x9587e30)
IP: HASH(0x94932a8)	Size: Bytes	Files: 
IP: 20.20.20.20	Size:149.53 MBytes	Files: HASH(0x9587e30)
IP: HASH(0x962eff8)	Size: Bytes	Files:
Besonders auffällig: Jede zweite Zeile hat als IP HASH(...). Weiterhin kann die Sizegröße nicht stimmen, da sie bei allen Hashes gleich ist.

Vielleicht ist auch noch nicht ganz klar, was ich bezwecken will. Daher:
in dem 2D-Array Rows stehen je Zeile IP, filename, Größe
row[1] == IP
row[-1] == Größe
row[-2] == Filename

diese sollen nun nach IP aggregiert werden mittels dem Hash. Ich will also je IP Adresse wissen, welche Dateinamen zugeordnet sind und wieviel Byte das insgesamt sind.

Ich denke, dass es nur eine Kleinigkeit ist. Ich bin dennoch für jede Hilfe dankbar!

Vielen Dank und Grüße
kickerxy
 

deepthroat

Erfahrenes Mitglied
#2
Hi.

Perl:
	else #neuer key, hash ergänzen
	{
		%aggSize = (%aggSize, @$row[1] => { size => @$row[-1], name => "\"".@$row[-2]."\""}); #hash of hash
	}
Du weist hier eine Liste an den Hash zu.

Das führt dazu das du zwei Key-Value Paare in den Hash einfügst:

Code:
%aggSize => <NICHTS>
und

Code:
@$row[1] => { size => @$row[-1], name => "\"".@$row[-2]."\""}
Laß einfach das %aggSize aus der Liste raus. Dann sind die IP HASH(..) Zeilen aus der Ausgabe verschwunden.

Dann kannst du natürlich auch die
Code:
%hash{ 'key' } = 'value'
Syntax verwenden.

Dann ordnest du den IP Adressen einen Hash zu. Das ist unzweckmäßig, da dann natürlich die Werte für size und name immer überschrieben werden.

Deine Datenstruktur sollte vielmehr so aussehen:

Code:
HASH = { IP-Adresse => ([size, name], ...), ... }
 

kickerxy123

Erfahrenes Mitglied
#3
Hi,

danke für deine Hilfe.
Ich habe nun meinen Code umgebaut in (die Unterscheidung existing<.>not brauche ich wohl gar nicht):
Code:
my %hash = undef;

foreach my $row(@rows) 
{
      $hash{@$row[1]}  = [ "".(int($hash{@$row[1]}[0])+int(@$row[-1])), $hash{@$row[1]}[1] . ",\"".@$row[-2]."\""]; 
}
 

foreach my $ip(%hash)
{
      printf "IP: " . $ip . "\tSize:" . formatBytes(@$ip[0]) . "\tFiles: " . @$ip[1] . "\n";
}
Ergebnis:
Code:
IP: 1.1.1.1	Size: Bytes	Files: 
IP: ARRAY(0x8642440)	Size:245.31 MBytes	Files:a,b,v
IP: 1.1.1.2	Size: Bytes	Files: 
IP: ARRAY(0x8652818)	Size:1.15 GBytes	Files: x,z
Wie man unschwer erkennt passt es leider noch nicht ganz... Was genau mache ich noch falsch?

Danke und VG
kickerxy



#edit: Habe meinen Fehler selbst erkannt. Er wird beim durchiterieren wohl das 2D Array als 1D Array hintereinander interpretieren (so wie in C und anderen Sprachen). Lösung:

Code:
foreach my $ip(keys %hash)
{
	printf "IP: " . $ip . "\tSize:" . formatBytes($hash{$ip}[0]) . "\tFiles: " . $hash{$ip}[1] . "\n";

}
Das Wort Keys ist hier entscheidend.

Danke nochmals.
 
Zuletzt bearbeitet:

Neue Beiträge