Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Looping trough Array Key/Value Pairs

by maikelnight (Sexton)
on Sep 25, 2017 at 10:45 UTC ( [id://1200031]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks, i am new to perl...I have an array which Dumps to this:

$VAR1 = { '_shards' => { 'skipped' => 0, 'successful' => 5, 'total' => 5, 'failed' => 0 }, 'hits' => { 'hits' => [ { '_id' => 'AV6SrwuTv7sBjjRqMiW1', '_source' => { 'request' => '/inde +x.php', 'clientip' => '192. +168.1.1' }, '_type' => 'nginx', '_index' => 'nginx-2017.09.18', '_score' => '4.238926' }, { '_id' => 'AV6UL-DOv7sBjjRqMidb', '_source' => { 'clientip' => '192. +168.1.1', 'request' => '/' }, '_score' => '4.189655', '_type' => 'nginx', '_index' => 'nginx-2017.09.18' } ], 'total' => 2, 'max_score' => '4.238926' }, 'took' => 0, 'timed_out' => undef };

I try to get the key/values from '_source' but i dont know how. I can dump for example with: "print Dumper $_->{'hits'}{'hits'}->[0]->{'_source'};" and foreach, and get the pair from [0] and i can alter [0] to 1 and get the other pair.

foreach $_(@testarray) { print Dumper $_->{'hits'}{'hits'}->[0]->{'_source'}; }

But how can i loop through to get all the values? I tried to loop with foreach and a counter but it didnt work, it always gives [0] (indeed i might make a mistake). May i ask one to get me some help how to? Thanks a lot.

Replies are listed 'Best First'.
Re: Looping trough Array Key/Value Pairs
by haukex (Archbishop) on Sep 25, 2017 at 10:55 UTC

    See item 2 under Using References (or "Use Rule 1" in perlreftut), you can dereference any complex array references with @{...} and hash references with %{...}:

    for my $hit ( @{ $VAR1->{hits}{hits} } ) { print "hit: '$hit->{_id}'\n"; for my $key ( keys %{ $hit->{_source} } ) { my $val = $hit->{_source}{$key}; print "key: '$key', val: '$val'\n"; } }

    Outputs:

    hit: 'AV6SrwuTv7sBjjRqMiW1' key: 'request', val: '/index.php' key: 'clientip', val: '192.168.1.1' hit: 'AV6UL-DOv7sBjjRqMidb' key: 'request', val: '/' key: 'clientip', val: '192.168.1.1'

      Hi, first i'd like to say thanks to anyone who help here! I need to review all the stuff. I would like to ask Haukex -> i tried what you mentioned successfuly, but in my code it didnt work. I believe its because i posted the output of Data::Dumper ($VAR1). In my test case i did:

      @testarray = { '_shards' => { 'skipped' => 0, 'successful' => 5, 'total' => 5, 'failed' => 0 }, 'hits' => { 'hits' => [ { '_id' => 'AV6SrwuTv7sBjjRqMiW1', '_source' => { 'request' => '/inde +x.php', 'clientip' => '192. +168.1.1' }, '_type' => 'nginx', '_index' => 'nginx-2017.09.18', '_score' => '4.238926' }, { '_id' => 'AV6UL-DOv7sBjjRqMidb', '_source' => { 'clientip' => '192. +168.1.1', 'request' => '/' }, '_score' => '4.189655', '_type' => 'nginx', '_index' => 'nginx-2017.09.18' } ], 'total' => 2, 'max_score' => '4.238926' }, 'took' => 0, 'timed_out' => undef };

      And in that case what you mentioned dont work. I see you use $VAR1 (which works) but the goal would be to loop through an array with many elements....

        @testarray = { '_shards' => { 'skipped' => 0, 'successful' => 5, 'total' => 5, 'failed' => 0 }, ... };
        And in that case what you mentioned dont work. I see you use $VAR1 (which works) but the goal would be to loop through an array with many elements....

        A mental adjustment is needed here: initialized as shown,  @testarray is an array with a single element, an anonymous hash reference. If you can accommodate this properly in your code (or properly deal with an array with multiple elements), you will be on your way to your goal.

        Remember: Data::Dumper and Data::Dump (my favorite) are your friends.

        Update: Try this untested variation to haukex's posted code:

        for my $hashref (@testarray) { for my $hit ( @{ $hashref->{hits}{hits} } ) { print "hit: '$hit->{_id}'\n"; for my $key ( keys %{ $hit->{_source} } ) { my $val = $hit->{_source}{$key}; print "key: '$key', val: '$val'\n"; } } }


        Give a man a fish:  <%-{-{-{-<

Re: Looping trough Array Key/Value Pairs
by Discipulus (Canon) on Sep 25, 2017 at 11:28 UTC
    Hello maikelnight and welcome to the monastery and to the wonderful world of Perl!.. well you are year since 2011 but this is your first post so a bit late welcome..

    You already received an optimal answer by haukex and I can just add some hints.

    If you frequently deal with complex datastructure I suggest to install Data::Dump and use it instead of the CORE module Data::Dumper. Data::Dump ships with the handy dd method: the output is better understandable and you do no need anymore the print used for the core module.

    The use of $_ in your statement foreach $_(@testarray) is redundant: foreach already fill $_ for you. Even better use a named variable in your loops: foreach my $element (@array..

    Lastly, when when you cycle a complex datastructure you can profit of the ref function that return ARRAY for named or unnamed (anonymous declared with [] ) arrays, HASH for anonymous and named hashes and can return other useful values.

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Looping trough Array Key/Value Pairs
by 1nickt (Canon) on Sep 25, 2017 at 13:35 UTC

    I have an array which Dumps to this

    What you show is the contents of hashref, not an array. You're referring perhaps to the anonymous array under 'hits'?

    Let's say your hashref that you get back from Elasticsearch is called $results. Let's assume you want to know which IPs requested which resources, and you'd like a count. The following parses your hashref and produces another hash containing your desired data.

    use strict; use warnings; use feature 'say'; use Data::Dumper; $Data::Dumper::Indent = $Data::Dumper::Sortkeys = 1; my $results = load_results(); my %interesting; # extract interesting data, record count of hits per IP per request for my $hit ( map { $_->{'_source'} } @{ $results->{'hits'}->{'hits'} +} ) { $interesting{ $hit->{'request'} }->{ $hit->{'clientip'} }++; } say Dumper \%interesting; sub load_results { return { '_shards' => { 'skipped' => 0, 'successful' => 5, 'total' => 5, 'failed' => 0 }, 'hits' => { 'hits' => [ { '_id' => 'AV6SrwuTv7sBjjRqMiW1', '_source' => { 'request' => '/inde +x.php', 'clientip' => '192. +168.1.1' }, '_type' => 'nginx', '_index' => 'nginx-2017.09.18', '_score' => '4.238926' }, { '_id' => 'AV6SrwuTv7sBjjRqMiW1', '_source' => { 'request' => '/inde +x.php', 'clientip' => '192. +168.1.1' }, '_type' => 'nginx', '_index' => 'nginx-2017.09.18', '_score' => '4.238926' }, { '_id' => 'AV6UL-DOv7sBjjRqMidb', '_source' => { 'clientip' => '192. +168.1.1', 'request' => '/' }, '_score' => '4.189655', '_type' => 'nginx', '_index' => 'nginx-2017.09.18' }, { '_id' => 'AV6SrwuTv7sBjjRqMiW1', '_source' => { 'request' => '/', 'clientip' => '192. +168.1.2' }, '_type' => 'nginx', '_index' => 'nginx-2017.09.18', '_score' => '4.238926' }, ], 'total' => 2, 'max_score' => '4.238926' }, 'took' => 0, 'timed_out' => undef }; } # end sub __END__
    The key lines are:
    for my $hit ( map { $_->{'_source'} } @{ $results->{'hits'}->{'hits'} +} ) { $interesting{ $hit->{'request'} }->{ $hit->{'clientip'} }++; }
    To understand a complex expression involving a loop it's often helpful to read from the right to the left.
    • Here you start with the value of $results->{'hits'}->{'hits'}, which is the anonymous array you're interested in.
    • This is dereferenced with @{ ... }, which turns it into a regular array we can work with.
    • map then loops through this array, assigning the value of each element to $_, and then returning the result of the expression $_->{'_source'}, which is the hashref containing the two k-v pairs you want.
    • Then on the next line you populate the hash %interesting with a key for each unique value of 'request', and a value of a hashref, with its own subkeys for each 'clientip' that requested the resource.
    • The value for each of those subkeys is incremented by one for each time the record is found (starting from 0 which is magically auto-initialized by Perl).
    This gives a data structure like this (I added a couple of hits to flesh out the results):
    $VAR1 = { '/' => { '192.168.1.1' => 1, '192.168.1.2' => 1 }, '/index.php' => { '192.168.1.1' => 2 } };
    Which you can then loop through and print in a report or whatever.
    [ ... ] for my $hit ( map { $_->{'_source'} } @{ $results->{'hits'}->{'hits'} +} ) { $interesting{ $hit->{'request'} }->{ $hit->{'clientip'} }++; } for my $resource ( keys %interesting ) { say "Resource: $resource"; foreach my $ip ( keys %{ $interesting{ $resource } } ) { say "\t$ip made $interesting{ $resource }->{ $ip } requests"; } }
    Output:
    Resource: / 192.168.1.1 made 1 requests 192.168.1.2 made 1 requests Resource: /index.php 192.168.1.1 made 2 requests

    Hope this helps!


    The way forward always starts with a minimal test.
Re: Looping trough Array Key/Value Pairs
by GrandFather (Saint) on Sep 25, 2017 at 21:51 UTC

    You use the word "array" but show a dump of a hash which points to some PHP background. Perl uses distinct data types for arrays (a stack of pigeon boxes) and hashes (an structure containing key, value pairs). So in Perl you access array elements by index (always a number) and hash values by key (always a string). To get just the values from a has use values %myHash and to get just the keys use keys %myHash. See https://perldoc.perl.org/functions/keys.html and https://perldoc.perl.org/functions/values.html.

    Premature optimization is the root of all job security
Re: Looping trough Array Key/Value Pairs
by AnomalousMonk (Archbishop) on Sep 25, 2017 at 15:24 UTC
Re: Looping trough Array Key/Value Pairs
by thanos1983 (Parson) on Sep 25, 2017 at 11:57 UTC

    Hello maikelnight,

    Fellow monks have provided you with answers but I also like to propose another way to retrieve data from hashes each. Give it a try if you like it.

    Hope this helps, BR.

    Seeking for Perl wisdom...on the process of learning...not there...yet!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1200031]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2024-04-25 07:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found