1
#!/usr/bin/perl

use strict;
use warnings;

my %ani_hash = (
    'machine_results' => [
        {
            'status'  => 'Failed install',
            'machine' => '23.73.134.235',
            'seconds' => '20',
            'try'     => '1'
        },
        {
            'status'  => 'Failed install',
            'machine' => '23.73.134.140',
            'seconds' => '20',
            'try'     => '1'
        }
    ],
    'description' => 'MC-5897'
);

get_elements( \%ani_hash );

sub get_elements
{
    my $hashref1 = shift;

    my %hashref2 = %$hashref1;
    print "%hashref1\n";

    foreach my $machineresult ( keys %hashref2 ) {
        foreach my $machineresult2 ( keys %{ $hashref2{$machineresult} } ) {
            print "$hashref2{$machineresult}{$machineresult2}\n";
        }
    }
}

Output: 

    HASH(0x1e9fe58)
    Not a HASH reference at ./hashref.pl line 62.
    Can't use string ("MC-5897") as a HASH ref while "strict refs" in use at ./hashref.pl line 62.

I want to loop through all the key value pairs and get their values. I don't want to use the dumper method to get the values, I want to get these by looping method. Any help would be appreciated. Thanks

I did this to fix the issue and get the contents of 'machine_results'.

print "description: $hashref2{description}\n";

foreach my $machineresult( sort keys%hashref2){
    foreach my $array (@{ $hashref2{$machineresult} }){
        foreach my $array1( sort keys%{$array} ){
            print "key is $array1 and it's value is $array->{$array1}`",
                  "enter code here`\n";    
        }
        print "\n";
    }
}
zdim
  • 64,580
  • 5
  • 52
  • 81
hitman99
  • 65
  • 7
  • `$hashref2{$machineresult}` is a reference to an array, so `keys %{ $hashref2{$machineresult} }` makes no sense. You want `@{ $hashref2{$machineresult} }` – ikegami Mar 03 '16 at 19:34
  • It _seems_ to me that your `%ani_hash` always has 2 elements, `machine_results` and `description`, and you merely want to iterate over the array(ref) in `machine_results`, right? – PerlDuck Mar 03 '16 at 19:45
  • @DavidO: I have a problem with your sarcasm, as well as the implication that *recursion* is the only way to process such a structure – Borodin Mar 03 '16 at 20:15
  • Thank you @ikegami for your help, I have edited my post. Let me know if this looks good. – hitman99 Mar 03 '16 at 22:50
  • Just run the code yourself and you'll see `Can't use string ("MC-5897") as an ARRAY ref while "strict refs" in use at ./so.pl line 36.` That's the line `foreach my $array`. It happens because the code tries to interpret `$hashref2{'description'}` as an arrayref which it isn't. It's a string. – PerlDuck Mar 04 '16 at 12:08

2 Answers2

1

I assume that your %ani_hash always has 2 elements, machine_results and description, and you merely want to iterate over the array(ref) in machine_results. If that is true, the following can help:

sub get_elements
{
    my $hashref = shift;

    print "Description: $hashref->{description}\n";

    my $count=0;
    foreach my $result ( @{ $hashref->{machine_results} } ) {
        print $count++, ': ';
        foreach my $key ( sort keys %{$result} ) {
            print "$key=$result->{$key}, ";
        }
        print "\n";
    }
}

get_elements( \%ani_hash );

Output:

Description: MC-5897
0: machine=23.73.134.235, seconds=20, status=Failed install, try=1, 
1: machine=23.73.134.140, seconds=20, status=Failed install, try=1, 

Explanation:

  • $hashref->{machine_results} is the arrayref with your machine results.
  • @{ $hashref->{machine_results} } dereferences it to an array you can iterate over, so
  • $result is one of these array items which in turn is a reference to a hash
  • with %{$result} it gets dereferenced to a hash and we iterate over the (sorted) keys

This, of course, assumes that your data structure is at it is, i.e. machine_results holds an arrayref with hashrefs inside.

PerlDuck
  • 5,610
  • 3
  • 20
  • 39
  • Thank you @Perl Dog for your help. I modified the code and edited the post. Let me know if it looks good. – hitman99 Mar 03 '16 at 22:52
0

Your code is not unpacking the nested hash correctly. For a hash %h = ('key' => [1, 2]) the value associated with key is a reference to an (anonymous) array, so a scalar. See links at the end.

To get to the content of the array we need to de-reference it. Then those array elements are hashrefs themselves and they need to be de-referenced as well

sub get_elements 
{    
    my %hash = %{ $_[0] };
    print "description: $hash{'description'}\n";
    foreach my $mach_res (keys %hash)
    {   
        next if $mach_res eq 'description';
        foreach my $elem ( @{$hash{$mach_res}} ) { 
            my %mach_detail = %$elem;
            print "\t---\n";
            foreach my $key (sort keys %mach_detail) {
                print "\t$key => $mach_detail{$key}\n";
            }   
        }           
    }   
}

The hashref is used to get a new 'in-sub' copy %hash. This is some protection from errors, since the original data cannot be changed. But if you want subs to directly change data whose reference they get then you need to work with the reference itself. This prints

description: MC-5897
        ---
        machine => 23.73.134.235
        seconds => 20
        status => Failed install
        try => 1
        ---
        machine => 23.73.134.140
        seconds => 20
        status => Failed install
        try => 1

Code will process any additional keys other than 'description'. If you'd like to align the printout, you can first pull the maximum length of a key (word) in it, then use printf for details.

use List::Util qw(max);
foreach my $elem ( @{$hash{$mach_res}} ) {
    my %mach_detail = %$elem;
    my $max_len = max( map { length } keys %mach_detail );
    print "\t---\n";
    foreach my $key (sort keys %mach_detail) {
        #print "\t$key => $mach_detail{$key}\n";
        printf("\t%${max_len}s => %s\n", $key, $mach_detail{$key});
    }
}

Module List::Util is used to get max function. Now you get

Description: MC-5897
        ---
        machine => 23.73.134.235
        seconds => 20
         status => Failed install
            try => 1
        ---
        machine => 23.73.134.140
        seconds => 20
         status => Failed install
            try => 1

A couple of related recent SO resources: a nested hash/array and an array of hashes.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Thank you @zdim for the references and the code :). Will certainly look it up. – hitman99 Mar 03 '16 at 22:53
  • @hitman99 Most welcome. Good references for this stuff are precious, before you get to read thoroughly from comprehensive resources. Luckily, SO has a lot of good, hands-on, to-the-point introductions. Finishing my edit so that this prints your whole structure nicely. I hope this was useful for you. – zdim Mar 03 '16 at 22:55
  • This was certainly helpful. I agree on your point about SO. Thank you :) – hitman99 Mar 03 '16 at 23:11
  • I cannot tag you @zdim somehow I don't know why :\ – hitman99 Mar 03 '16 at 23:11
  • I already upvoted your answer, but it seems that I need some more badges to do so(given I am new here). :) – hitman99 Mar 04 '16 at 16:48
  • I did that. Thank you for bringing that to my attention – hitman99 Mar 24 '16 at 20:49
  • @hitman99 Thanks for that and for the feedback. If any questions about this pop up please let me know. – zdim Mar 24 '16 at 20:53