2013-07-19 5 views
0

Je suis nouveau à l'aide de perl et j'essaie de construire un hachage d'un hachage à partir d'un TSV. Mon processus actuel consiste à lire dans un fichier et à construire un hachage, puis à l'insérer dans un autre hachage.Construire hachage de hachage en perl

my %hoh =(); 
    while (my $line = <$tsv>) 
    { 
     chomp $line; 
     my %hash; 
     my @data = split "\t", $line; 

     my $id; 
     my $iter = each_array(@columns, @data); 

     while(my($k, $v) = $iter->()) 
     { 
     $hash{$k} = $v; 
     if($k eq 'Id') 
     { 
      $id = $v; 
     } 
     } 

     $hoh{$id} = %hash; 
    } 
    print "dump: ", Dumper(%hoh); 

Ce sorties:

dump 
$VAR1 = '1234567890'; 
$VAR2 = '17/32'; 
$VAR3 = '1234567891'; 
$VAR4 = '17/32'; 
..... 

Au lieu de ce que j'attendais:

dump 
{ 
    '1234567890' => { 
        'k1' => 'v1', 
        'k2' => 'v2', 
        'k3' => 'v3', 
        'k4' => 'v4', 
        'id' => '1234567890' 
        }, 
    '1234567891' => { 
        'k1' => 'v1', 
        'k2' => 'v2', 
        'k3' => 'v3', 
        'k4' => 'v4', 
        'id' => '1234567891' 
        }, 
    ........ 
}; 

Ma compréhension limitée est que lorsque je fais $hoh{$id} = %hash; son insertion dans une référence à% hachage? Qu'est-ce que je fais mal? Y a-t-il aussi une façon plus succint d'utiliser mes colonnes et tableaux de données en tant que clés, paires de valeurs dans mon objet% hash?

-Merci à l'avance, Niru

Répondre

2

Pour obtenir une référence, vous devez utiliser \:

$hoh{$id} = \%hash; 

%hash est le hachage, pas la référence. Dans le contexte scalaire, il renvoie la chaîne X/Y wre X est le nombre de compartiments utilisés et Y le nombre de tous les segments dans le hachage (c'est-à-dire rien de utile).

+0

Vous devrez également passer une référence lors de l'appel 'Dumper'. – cjm

+0

Merci, cela a résolu mon problème. – Niru

2

Pour obtenir une référence à une variable de hachage, vous devez utiliser \%hash (comme dit choroba).

Une façon plus succincte d'attribuer des valeurs aux colonnes est à attribuer à un hash slice, comme ceci:

my %hoh =(); 
while (my $line = <$tsv>) 
{ 
    chomp $line; 
    my %hash; 
    @hash{@columns} = split "\t", $line; 
    $hoh{$hash{Id}} = \%hash; 
} 
print "dump: ", Dumper(\%hoh); 

Une tranche de hachage (@hash{@columns}) signifie essentiellement la même chose que ($hash{$columns[0]}, $hash{$columns[1]}, $hash{$columns[2]}, ...) jusqu'à cependant de nombreuses colonnes que vous avez . En lui assignant, j'affecte la première valeur de split à $hash{$columns[0]}, la deuxième valeur à $hash{$columns[1]}, et ainsi de suite. Il fait exactement la même chose que votre boucle while ... $iter, juste sans la boucle explicite (et n'extrait pas le $id).

Il n'est pas nécessaire de comparer chaque $k à 'Id' à l'intérieur d'une boucle; il suffit de le stocker dans le hachage comme un champ normal et l'extraire ensuite avec $hash{Id}. (A part: Votre tête de colonne Id ou id Vous utilisez Id dans votre boucle, mais id dans la sortie attendue?).

Si vous ne souhaitez pas conserver le champ Id dans les entrées individuelles, vous pouvez utiliser delete (qui supprime la clé du hachage et renvoie la valeur):

$hoh{delete $hash{Id}} = \%hash; 
+0

Désolé un peu confus par la façon dont cela fonctionne. Ce qui se passe avec: @hash {@columns}. Donc convertir% hash en un tableau et pour chaque valeur dans la colonne itérer sur le résultat de la scission et insérer séquentiellement dans @hash? aussi ce qui se passe ici: $ hoh {$ hash {Id}} = \% hash; Comment sait-il trouver la valeur de la colonne id i.ie 1234567890 et l'utiliser comme clé pour hoh? – Niru

+0

Lire la documentation sur les tranches de hachage auxquelles je suis lié. '@hash {@columns}' est comme '($ hash {$ columns [0]}, $ hash {$ colonnes [1]}, $ hash {$ columns [2]}, ...)'. Il ne transforme pas '% hash' en un tableau, il utilise simplement le tableau sigil. – cjm

+0

Il sait comment trouver la colonne id parce que c'est dans '$ hash {Id}', que j'utilise comme clé lors de l'assignation du hashref à '$ hoh {$ hash {Id}}'. – cjm

1

Consultez la documentation incluse dans Perl. La commande perldoc est très utile. Vous pouvez également consulter la page Web Perldoc.

L'un des tutoriels est un tutoriel sur Perl references. Tout cela aide à clarifier beaucoup de vos questions et à expliquer le référencement et le déréférencement.

Je vous recommande également de regarder CPAN. Ceci est une archive de divers modules Perl pouvant effectuer de nombreuses tâches diverses. Regardez Text::CSV.Ce module fera exactement ce que vous voulez, et même s'il dit "CSV", il fonctionne aussi avec des fichiers séparés par des tabulations.

Vous avez manqué de mettre un slash devant votre hash que vous essayez de faire référence. Vous avez:

$hoh{$id} = %hash; 

veulent probablement:

$hoh{$id} = \%hash; 

aussi, lorsque vous faites une Data::Dumper d'un hachage, vous devez le faire sur une référence à un hachage. En interne, les hachages et les tableaux ont des structures similaires lorsqu'un vidage Data :: Dumper est effectué.

Vous avez:

print "dump: ", Dumper(%hoh); 

Vous devriez avoir:

print "dump: ", Dumper(\%hoh); 

Ma tentative du programme:

#! /usr/bin/env perl 
# 
use warnings; 
use strict; 
use autodie; 
use feature qw(say); 
use Data::Dumper; 

use constant { 
    FILE => "test.txt", 
}; 

open my $fh, "<", FILE; 

# 
# First line with headers 
# 

my $line = <$fh>; 
chomp $line; 
my @headers = split /\t/, $line; 
my %hash_of_hashes; 

# 
# Rest of file 
# 
while (my $line = <$fh>) { 
    chomp $line; 
    my %line_hash; 
    my @values = split /\t/, $line; 
    for my $index ((0..$#values)) { 
     $line_hash{ $headers[$index] } = $values[ $index ]; 
    } 
    $hash_of_hashes{ $line_hash{id} } = \%line_hash; 
} 

say Dumper \%hash_of_hashes; 
+0

Ah, je ne savais pas que CSV gère aussi TSV, je ne voulais pas faire de conversion manuelle entre l'obtention et l'ouverture du fichier. Merci. – Niru

+0

Sauf si vous avez des guillemets dans votre TSV (ce qui est rare), 'Text :: CSV' est trop puissant pour analyser TSV. 'split' fonctionne très bien pour ça. – cjm

+0

@cjm Regardez mon code qui n'utilise pas 'Text :: CSV'. Je dois lire dans mon en-tête, puis faire correspondre mon en-tête à chaque ligne, afin que je puisse construire un hachage. Text :: CSV gère ça pour moi. Mon code échouera si une ligne a plus de champs qu'un autre. Je peux le manipuler avec 'Text :: CSV'. 'Text :: CSV' gère tout pour moi et facilite mon travail de programmation. La chose la plus difficile à propos de 'Text :: CSV' est sa documentation incomplète, et le fait qu'il ne s'agit pas d'un module standard, il peut donc ne pas être disponible pour vous. –

0

Vous ne devez stocker une référence à une variable si vous donc dans la dernière ligne avant que la variable ne disparaisse de la portée. Dans votre script, vous déclarez %hash dans la boucle while, plaçant ainsi cette déclaration comme la dernière dans la boucle est sûre:

$hoh{$id} = \%hash; 

Si ce n'est pas la dernière déclaration (ou vous n'êtes pas sûr qu'il est sûr), créer une structure anonyme pour maintenir le contenu de la variable:

$hoh{$id} = { %hash }; 

Cela fait une copie de %hash, ce qui est plus lent, mais toute modification ultérieure ne sera pas ce que vous effet stocké.