clintonm9 has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to scan through a hash ref and get the value of certain elements (Regardless of the data structure). Below is some sample code that will hopefully explain what I am trying to accomplish. I have a data structure. I want to be able to get the value by just sending to a sub routine
my @return = &sub('Root->PersonA->Pets');
then it could send me back all the results for that locations. In the data below it would send back PetA and PetB. Notice the different structure between PersonA and PersonB. If I sent Root->PersonB->Pets I would get back PetAA and PetBB without having to change anything. In the example below I convert everything to xml then use the XMLPAth to find the results. I do not think this is the best way but not sure how else to do it. Any ideas?
use Data::Dumper; use XML::Simple; use XML::XPath; use XML::XPath::XMLParser; my $perl_datastructure = { Root => { 'PersonA' => { Pets => [ PetA, PetB, ], }, 'PersonB' => [ { Pets => PetAA, }, { Pets => PetBB, }, ], }, }; print Dumper($perl_datastructure); $xmlstring = XMLout($perl_datastructure, NoAttr => 1, KeepRoot => +1); #print $xmlstring; my $xp = XML::XPath->new( xml => $xmlstring ); my $nodeset = $xp->find("//Root/PersonB/Pets"); foreach my $node ($nodeset->get_nodelist) { my $perldata = XMLin( XML::XPath::XMLParser::as_string($node) +); print $perldata, "\n"; }

Replies are listed 'Best First'.
Re: Perl Hash Ref
by jethro (Monsignor) on Nov 19, 2009 at 04:06 UTC
    This cries out for recursive treatment:
    #!/usr/bin/perl use strict; use warnings; my $perl_datastructure = { ... }; sub finder { my ($search,$p)= @_; return(@$p) if ($search eq '' and ref $p eq 'ARRAY'); return($p) if ($search eq ''); $p= $perl_datastructure if (not defined $p); if (ref $p eq 'HASH') { $search=~ s/^(.*?)(->|$)//; my $bit=$1; return finder($search,$p->{$bit}) if (exists $p->{$bit}); return(); } if (ref $p eq 'ARRAY') { my @results=(); foreach my $a ( @$p ) { push @results, finder($search,$a); } return @results; } #here should be error messages or further #code to handle refs and scalars } print join(' ',finder('Root->PersonA->Pets')),"\n"; print join (" ",finder('Root->PersonB->Pets')),"\n";
    prints
    PetA PetB PetAA PetBB
      thank you so much! This works great. I havent been able to brake it yet!
Re: Perl Hash Ref
by ikegami (Patriarch) on Nov 19, 2009 at 04:02 UTC

    I want to be able to get the value by just sending my @return = &sub('Root->PersonA->Pets'); to a sub routine

    Isn't that what findnodes does?

    use strict; use warnings; use XML::Simple qw( XMLout ); use XML::LibXML qw( ); my $perl_datastructure = { Root => { 'PersonA' => { Pets => [qw( PetA PetB )], }, 'PersonB' => [ { Pets => 'PetAA', }, { Pets => 'PetBB', }, ], }, }; my $xml = XMLout($perl_datastructure, NoAttr => 1, KeepRoot => 1); my $parser = XML::LibXML->new(); my $doc = $parser->parse_string($xml); my $root = $doc->documentElement(); print $_->textContent(), "\n" for $root->findnodes('/Root/PersonA/Pets'); print("--\n"); print $_->textContent(), "\n" for $root->findnodes('/Root/PersonB/Pets');
    PetA PetB -- PetAA PetBB
Re: Perl Hash Ref
by ikegami (Patriarch) on Nov 19, 2009 at 03:57 UTC

    Notice the different structure between PersonA and PersonB.

    Yeah, you appear to have buggy code. Specifically, you forgot to specify ForceArray => [qw( Pets )]I see, you have two Person records for the same Person. That's a bit odd.

    Also, having <PersonA> and <PersonB> makes no sense. Tag names shouldn't be data. You should have something like <Person name="A"> and <Person name="B">.