2010-08-27 5 views
3

Je veux supprimer un élément d'un hachage (de toute profondeur) qui a la première clé comme $key[0], la deuxième clé comme $key[1] etc, jusqu'à ce que @key est terminée. Par exemple, si @key=(23,56,78) puis je veux manipuler $hash{23}{56}{78}. Je ne sais pas à l'avance combien d'éléments @key a.Perl accéder/changer profond hachage par liste de tableau

J'ai essayé d'utiliser ce qui suit:

my %the_path; 
my $temp=\%the_path; 
for(my $cline=0;$cline<=$#keys;$cline++){ 
    my $cfolder=$keys[$cline]; 
    $temp->{$cfolder}={}; 
    $temp=$temp->{$cfolder}; 
} 

Mais, je ne suis pas sûr de savoir comment manipuler l'élément à ici. Comment je fais ça?

+1

scalaire @key vous donne la longueur, n'est-ce pas? – Jerome

+0

ressemble à des devoirs. avez-vous essayé de résoudre cela vous-même? –

+0

ce n'est pas des devoirs et j'ai essayé moi-même, oui. –

Répondre

5

Data::Diver existe exactement dans ce but.

my $last_hash = Data::Diver::Dive(\%hash, @keys[0..$#keys-1]); 
if ($last_hash) { delete $last_hash->{ $keys[-1] } } 
+0

Vous ne voulez pas dire: my $ hash_ref = \% hash; my $ last_hash = Données :: Diver :: Dive ($ hash_ref, @keys [0 .. $ # keys-1]); if ($ last_hash) {supprimer $ last_hash -> {$ keys [-1]}; } ? –

+0

@Hermann Ingjaldsson: merci – ysth

1

Vous devez parcourir l'arborescence de hashrefs en utilisant la valeur de clé suivante dans la liste comme clé à parcourir, jusqu'à ce que vous atteigniez l'avant-dernière touche de la liste; puis supprimez la valeur hashref associée à la dernière clé.

my $hash_ptr = $my_hash_ref; 
foreach my $key_num (0..$#keys) { 
    my $key = $keys[$key_num]; 
    if (exists $hash_ptr->{$key}) { 
     if ($key_num == $#keys) { 
      delete $hash_ptr->{$key}; 
     } else { 
      $hash_ptr = $hash_ptr->{$key}; # Go down 1 level 
     } 
    } else { 
     last; 
    } 
} 

Note: cela ne supprime pas les éléments de l'arbre hashref au-dessus du dernier, même si elles ne contiennent pas de touches plus. En d'autres termes, il supprime 1 nœud, PAS un chemin entier - il ne taille pas l'arbre, selon les mots d'Evan. Si ce n'est pas ce que vous vouliez dire, veuillez clarifier.

+0

Pourquoi la downvote? – DVK

+0

Juste retesté le code - cela fonctionne. Je ne sais pas ce que le downvote était pour – DVK

+0

vous avez un bug; aucune suppression ne devrait se produire pour '{a => {c => 42}}' avec les touches a, b, c (mais je n'ai pas downvote) – ysth

1

Voici un exemple avec récursion:

use strict; 
use warnings; 

my $hash = { foo => { here => 'there', bar => { baz => 100 } } }; 

## mutates input 
sub delete_hash { 
    my ($hash, $keys) = @_; 
    my $key = shift @$keys; 
    die "Stopped recursing $key doesn't exist" 
    unless exists $hash->{$key} 
    ; 
    scalar @$keys 
    ? delete_hash($hash->{$key}, $keys) 
    : delete $hash->{$key} 
    ; 
} 

delete_hash($hash, [qw/foo bar/]); 

use XXX; 
YYY $hash; 

La pile ne se développe et les appels de fonction ont un prix. Vous pouvez aparently atténuer ce avec la version de Perl de TCO avec ce code:

if (scalar @$keys) { 
    @_=($hash->{$key}, $keys); 
    goto &delete_hash; 
} 
else { 
    delete $hash->{$key} 
} 

Et, il convient également de noter qu'aucun de ce code élaguer l'arbre: si vous supprimez [qw/foo bar baz/] alors bar sera une référence de hachage vide.

foo: 
    bar: {} 
    here: there 
+0

vous pourriez, bien sûr, simplement dérouler la récursion dans une boucle si vous avez des problèmes de performance (auquel cas vous obtenez ma réponse :)). Bon libellé sur "élagage" - je vais le voler, désolé :) – DVK

+0

Vous pouvez toujours éliminer les sous-appels, dépend de votre objectif. Peut-être que je vais écrire un benchmark avec la variante goto. –

Questions connexes