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

And for my second post.... I am coming unstuck on Hashes and Arrays. I am using XML:Simple to read in an xml file. In order to read my tree I have to set a flag 'forcearray'. The tree structure is like this:
<root> <page> <field> <ID></ID> <value></value> </field> </page> </root>
The root can have more many pages, and each page can have many fields. The fields only have the ID and value. I am trying to add a new field to a specific page. So far I have got:
my $pageobj=$data->{page}[$pagenumber]; my $fieldobj=$pageobj->{field};
when dumped this produces:
[ { 'value' => [ 'test' ], 'ID' => [ '0000001' ] }, { 'value' => [ 'test' ], 'ID' => [ '0000002' ] }, { 'value' => [ 'test' ], 'ID' => [ '0000003' ] }, { 'value' => [ 'test' ], 'ID' => [ '0000004' ] } ]
I have tried various ways of adding a new field to this, I would ideally like to build a new field from values I enter, and then add it to the array. I tried creating a new hash, and adding in a value and ID. Although I feel I am slipping up somewhere:
my $pageobj=$data->{page}[$pagenumber]; my $fieldobj=$pageobj->{field}; my %tmpF=(); my @tmpA; push(@tmpA,"0001"); %tmpF->{ID}=@tmpA; push(@$fieldobj,%tmpF);
After dumping this, it doesnt seem to have the desired result... Any help would be amazing TienLung

Replies are listed 'Best First'.
Re: Arrays and Hashes woes
by moritz (Cardinal) on Mar 16, 2009 at 11:07 UTC
    You need to assign/push references to hashs/arrays, not the things themselves.
    my %tmpF=(); my @tmpA; push(@tmpA,"0001"); %tmpF->{ID}=@tmpA; push(@$fieldobj,%tmpF);

    should be

    my %tmpF=(); my @tmpA; push(@tmpA,"0001"); %tmpF->{ID}=\@tmpA; # ^ push(@$fieldobj,\%tmpF); # ^

    Please read perlreftut to understand why you need references, and how they work.

      Many thanks, This has done the trick and reading that tutorial has shown me the light. Tien Lung
Re: Arrays and Hashes woes
by almut (Canon) on Mar 16, 2009 at 11:05 UTC
    %tmpF->{ID}=@tmpA; push(@$fieldobj,%tmpF);

    I suppose you need array/hash references (note the \ operator):

    $tmpF{ID} = \@tmpA; push(@$fieldobj, \%tmpF);

    (Also, the %tmpF->{ID} should be $tmpF{ID})

      Many thanks, using references solved the problem. I note that using:
      %tmpF->{ID}
      and
      $tmpD{ID}
      seems to make no difference, both work. What are your reasons for suggesting the bottom way over the top way? I am not trying to say you are wrong! far from it, I just want to know why you would do it that way :) TienLung

        The former syntax is deprecated (and rather unusual):

        $ perl -w -e "%tmpF->{ID} = 'foo'" Using a hash as a reference is deprecated at -e line 1.
Re: Arrays and Hashes woes
by Jenda (Abbot) on Mar 16, 2009 at 18:28 UTC
    use XML::Rules; my $parser = XML::Rules->new( stripspaces => 3, rules => { 'ID,value' => 'content', field => sub {$_[1]->{ID} => $_[1]->{value}}, page => 'as array', root => sub {$_[1]->{page}}, } ); my $data = $parser->parse(\*DATA); use Data::Dumper; print Dumper($data); $data->[1]{Comments} = "many, of various quality"; # on the other hand the output is rather crazy. We have to convert the + data structure to a format containing all the tags. print $parser->ToXML(root => {page => [map { my $fields = $_; +{field => [map { +{ID => [$_], value => [$fields->{$_}]} } keys % +$fields]} } @$data]}, 0, ' '); # or, if I split it down a bit: print $parser->ToXML(root => {page => [map { hash2fields($_) } @$data] +}, 0, ' '); sub hash2fields { my $fields = shift; return {field => [map { +{ID => [$_], value => [$fields->{$_}]} } +keys %$fields]} } __DATA__ <root> <page> <field> <ID>fname</ID> <value>Jenda</value> </field> <field> <ID>lname</ID> <value>Krynicky</value> </field> </page> <page> <field> <ID>Site</ID> <value>PerlMonks</value> </field> <field> <ID>Nick</ID> <value>Jenda</value> </field> </page> </root>

    A little easier to access and modify the data (plus the data structure takes up less memory), the output is a bit scary. Use Data::Dumper to have a look at the structure passed to ->ToXML() to see how it works, it's quite similar to what XML::Simple produced.

    P.S.: If anyone has a clever idea how better to specify the datastructure->XML mapping I'm all ears.

    Update: I posted a meditation regarding the output at Datastructures to XML.

Re: Arrays and Hashes woes
by locked_user sundialsvc4 (Abbot) on Mar 16, 2009 at 16:27 UTC

    As you've just witnessed, “the secret, if there is one,” is references. The syntax of Perl is, typically, relaxed about such matters: it does not buttonhole you with a mandatory grammar lesson. (Yay!) The key idea is that... both arrays and hashes can (appear to) contain anything-at-all, or even to contain the same thing in many different structures and places (such that changes appear everywhere-at-once) because what is actually being tossed-about are references to these things. No matter how big-and-hairy is “the thing that the reference refers to,” and no matter how many other references might be doing the same, each reference is a small and tidy and efficient thing ... somewhat like a highly-civilized pointer with very good manners. Once you grok what is being done(!), it's natural, intuitive, resource-efficient, and of course, quite powerful.