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

I admit it I am completely baffled by the syntax of reading (retrieving values from) arrays of hashes. Defining them is easy enough but I'm missing something on the accessing side.

I'm trying to do a practical task, involving retrieving all the paths (in this case) (later messages) for a range of subversion revisions use SVN::Log;

I've declared (and invoked) the AoH/svn log retrieval for my repo and grabbed the data I'm interested in.

       my @revs=SVN::Log::retrieve($repoURL, $rev1, $rev2); # (svn::log declares the data structure described below)

Using komodo I can see that my array of hashes "@revs" contains this data structure and data:

--------- 1 @revs 2 ->[0] 3 ->[0] 4 ->{author} myid 5 ->{date} 2013-06-11 6 ->{message} CR-2020 Implement... 7 ->{paths} 8 ->{"/app/trunk/...} 9 ->{action} M 10 ->{revision} 31 11 ->[1] 12 ->[2] ... -------

I believe the array revs contains one member [0] which contains a hash %0 which contains 0-n keys each containing author, date, message(a hash), paths(a hash), revision.

Intuitively I want to extract %0 from the array and then work with the hash key/value pairs in that. I would use the %hash->{0}->{revision} to get "31" on line 10 or %hash->{0}->{paths}->{actions} to get to the M on line 9 above.

I don't understand how to work with the hashes when they are inside the array. Tried a bunch of things that basically resulted in "FAIL". Consulted a lot of tutorials (most notably and appreciated http://perldoc.perl.org/perldsc.html#HASHES-OF-ARRAYS section on Arrays of Hashes which was very well done but failed to get through my hard head).

But I think (judging by the data structure above) that my data is nested one level deeper than in the example. Am I being fooled by the extra level $0->{0}

I know $var=shift(@array) and $var=@array[0].
I know $var=%hash->{key} (probably better written as $hash->{key} which I accept but don't understand) The website above gives this example:

for $i ( 0 .. $#AoH ){ #I get this, for 0 - the total members count in the array of hashes for $role ( keys %{ $AoH[$i] } ) { # new variable $role defined to hold the keys print "elt $i $role is $AoH[$i]{$role}\n"; # print the hash index# the hash reference? is (value of $role) for t +hat index? } }

That example only seems to go three deep and I've got one more I think. Am I defining my array wrong?

So, though I understand arrays pretty well (I use them extensively) this is apparently beyond my meager skills.

How do I get to the paths and revisions out of the hash and into a variable so I can print them?

I really have spent about two days trying to sort this out on my own, (and that's not the first time I've tried to slay the hash dragon in my brain) but there's something here I just don't grok. Thank you in advance for considering my question.

Replies are listed 'Best First'.
Re: Array of Hashes Svn::Log confusion
by toolic (Bishop) on Jun 21, 2013 at 16:41 UTC
    I think you should try following the example in the SYNOPSIS in the SVN::Log POD:
    use Data::Dumper; use SVN::Log; my $revs = SVN::Log::retrieve($repoURL, $rev1, $rev2); print Dumper($revs);
    The retrieve function seems to return a reference, rather than array. I think that may be one source of your troubles. Also, show us the Data::Dumper output, which is probably more familiar to us.

    Also look at the test code for t/02basics.t in the CPAN files for more examples on how to descend the data structure.

    UPDATE: Here is an actual example that works on my svn repo:

    use warnings; use strict; use SVN::Log; use Data::Dumper; my $revs = SVN::Log::retrieve('my://repo', 1, 5); #print Dumper($revs); print $revs->[0]{revision}, "\n"; print $revs->[0]{date }, "\n"; print $revs->[0]{author }, "\n"; __END__ 1 2012-11-13 14:16:18 root
Re: Array of Hashes Svn::Log confusion
by NetWallah (Canon) on Jun 21, 2013 at 22:11 UTC
    In addition to Data::Dumper and friends, the perl debugger is an excellent tool to slowly dive into a data structure, and understand how to access it.

    Here is a debugger session, based on a made-up structure, similar to yours:
    I have interjected comments starting with >>

    $ perl -dE 'my @revs=([{author=>"myid",date=>"2013-06-21"},2,3]); say +$revs[0]{date}' Loading DB routines from perl5db.pl version 1.33 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(-e:1): my @revs=([{author=>"myid",date=>"2013-06-21"},2,3]); + say $revs[0]{date} >>>> Step into the initialization of the structure .... DB<1> s main::(-e:1): my @revs=([{author=>"myid",date=>"2013-06-21"},2,3]); + say $revs[0]{date} >>>>>*** Looking at the whole structure (Not a good idea if it is larg +e) DB<1> x @revs 0 ARRAY(0x1765270) 0 HASH(0x1765210) 'author' => 'myid' 'date' => '2013-06-21' 1 2 2 3 >>>>> Slow dive - We know @revs is an ARRAY - let us see what TYPE the + first (0) element is : DB<2> x ref $revs[0] 0 'ARRAY' >>>> OK - so it is an 'ARRAY' - lets examine the TYPE of the first (0) + index. DB<7> x ref $revs[0][0] 0 'HASH' >>> Aha - so $revs[0][0] is a HASHREF - lets see what KEYS are availab +le... DB<9> x keys %{ $revs[0][0] } 0 'date' 1 'author' >>>> Now we could do another "ref', but assuming this will result in a + target value, we simply try a key: DB<10> x $revs[0][0]{date} 0 '2013-06-21' >>> end the debug session with 'quit' DB<11> q
    so Now we know why "$revs[0]{date}" did not work.

                 "The trouble with the Internet is that it's replacing masturbation as a leisure activity."
            -- Patrick Murray

Re: Array of Hashes Svn::Log confusion
by gj2666 (Beadle) on Dec 01, 2014 at 20:41 UTC

    Because I came across my own question when I searched on this topic (having apparently given up the previous time) and I am now working on the same problem. I thought I would post my own solution (which I expect deserves to be savaged). So the poor schmuck wrestling with the same problem, has something to reflect on.

    For the record I read the suggestions (thanking contributors very much for your assistance). It must have been a bit above my ability to grok apparently. Yes, I may be a dope. For me it's always the syntax that baffles me and I confess I do not completely understand what I am doing even now, yet have to do it anyway.

    So here is my solution in part.

    use strict; use warnings; use SVN::Log; my $SVNrevs=SVN::Log::retrieve($branchURL, $firstTagRev, $secondTagRev +); $i=0; #need this because part the data structure demands it. foreach @$SVNrevs){ # the easy part print $SVNrevs->[$i]{revision}, ","; print $SVNrevs->[$i]{date }, ","; print $SVNrevs->[$i]{author }, "\n"; # This is the pissy bit. Getting the path and the action which are chi +ldren of the path element (the embedded hash) foreach my $recordpath ( keys $SVNrevs->[$i] ) { for my $path ( keys %{ $SVNrevs->[$i]{$recordpath} } ) { foreach my $action ($SVNrevs->[$i]{$recordpath}{$path}{'actio +n'}) { print "\t$action\t$path\n"; } } } my $message=$SVNrevs->[$i]{message }; $_=$message; s/\n/ /gi; #cleaning up random newlines in the message $message = $_; print "\n\t $message \n\n"; # } $i++; #iterate for the next record }

    Incidentally much of the example code (in the module doc and elsewhere including O'Reilly's Perl books for general concepts and examples, which I rely heavily upon) caused unexpected results (the way I applied it) such as duplicating the array (@SVNrevs) which was unnecessary, and I found the while loop to be tricky to figure out how many times to iterate. I made it work. It may not be elegant, but it's doing the job.

    Thanks all for your suggestions which in part finally helped me help myself.