1

I'm having trouble wrapping my brain around the data structure that is being returned...What I need to do is inspect the results, and modify a field if it is a HASH. Within 'results' any KEY that is a HASH with a KEY of 'null' and value of '1' needs to be changed to '0'. Below I have pasted some sample data from Data::Dumper of the return. In this instance, I want to change data in four different places. I have been dealing with this for some time and just can't figure it out....any help is appreciated.

$VAR1 = {
  'results' => [
    {
      'admin' => 'DUMMY DATA',
      'object' => 'DUMMY DATA',
      'ifDescr' => 'DUMMY DATA',
      'total_device' => {
        'null' => '1'
      },
      'ifIndex' => 'DUMMY DATA',
      'oper' => 'DUMMY DATA',
      'percent_online' => 'DUMMY DATA',
      'device_offline' => {
        'null' => '1'
      },
      'dataflow' => 'DUMMY DATA',
      'Map' => 'DUMMY DATA',
      'ifdevice' => 'DUMMY DATA',
      'device_online' => 'DUMMY DATA'
    },
    {
      'admin' => 'DUMMY DATA',
      'object' => 'DUMMY DATA',
      'ifDescr' => 'DUMMY DATA',
      'total_device' => {
        'null' => '1'
      },
      'ifIndex' => 'DUMMY DATA',
      'oper' => 'DUMMY DATA',
      'percent_online' => 'DUMMY DATA',
      'device_offline' => {
        'null' => '1'
      },
      'dataflow' => 'DUMMY DATA',
      'Map' => 'DUMMY DATA',
      'ifdevice' => 'DUMMY DATA',
      'device_online' => 'DUMMY DATA'
    }
  ]
};
Axeman
  • 29,194
  • 2
  • 42
  • 98
  • You now have two answers that interpret the question differently. To clarify, are you only looking for null => 1 at the specific level of hash in hash in results, or could you have null => 1 in hash in hash in hash ... in hash in results? ie, does it need to check recursively? – dorr Mar 20 '14 at 20:28

3 Answers3

1

I'm having trouble wrapping my brain around the data structure that is being returned...

You've already accepted an answer, I'm just going to clarify the interpretation of the Data::Dumper output:

  • Each {...} means a reference to a hash. You'll see key => value, as hash elements.
  • Each [...] represents a reference to an array. You'll see value, as array elements.

Breaking apart what you have:

$VAR = $VAR1 = {
   'results' => [
        ....       # This is an array reference
    ]

Or $VAR->{results} = [];

This is a hash with a single key of results. The hash has a reference to an array as it's value. So far:

$VAR1 = {
  'results' => [  # This is the [0] element in my array
    {
        ...       # This is a hash reference
    }
]
[                 # This is the [1] element in my array
    {
        ...       # This is a hash reference
    }

In this array, there are two values, each value is pointing to a hash reference:

$VAR->{results}->[0] = {};
$VAR->{results}->[1] = {};

In each of these arrays, the hash reference has 12 keys and values:

  • admin
  • dataflow
  • devices_online
  • object
  • ifDescr
  • ifDevice
  • ifIndex
  • oper
  • percent_online
  • Map
  • These are has references....
    • total_devices
    • devices_offline

The first 10 are simply key/value pairs. The last two are references to a further hash with a single key/value pair. The key is null. I assume this is a mistake of some sort.

Now I can refer to one of these items like this:

$VAR->{results}->[1]->{ifIndex} = 'DUMMY DATA';

Assuming the current structure, here's a way to refer to it in a loop:

my $VAR = some_function()                # Returns a reference to a hash.

for my $result ( keys %{ $VAR } ) {     # Dereference the hash reference...
   say "Key for results is '$result'";  # Only one result. And that's 'result'...
   my @array = $VAR->{$result};         # Dereference the array reference that hash points to
   for my $element ( 0..$#array ) {     # Now we get to the two elements in the array
      say qq(Looking at element #$element);
      my $hash_key = $array[$element];  # he hash reference that the array points to
      my %inner_hash = %{ $hash_key };  # Another dereference...
      for my $key ( keys %inner_hash" ) {
        say "\$VAR->{$result}->[$element]->{%hash_key} = "
           . $VAR->{$result}->[$element]->{%hash_key};
      }
   }
}

This won't entirely work because total_device and device_offline are again hash references. I should make an exception in my inner most loop, and if either of these are the key to my inner hash, I need to do another dereference to get to the hash. I'll let you work that out.

Of course, I know my structure, so I could write a program structure to handle it. If I didn't know the layout of my data structure, I would have to use the ref command to find out if I'm referring to a hash or an array, and dereference and loop accordingly. This is pretty much what Data::Dumper does.

I usually assume that such a complex structure comes from a class constructor, and I would expect to see the blessed class name of the object in such a Data::Dumper dump. In that case, I would tell you to use the methods for that class and not to deconstruct the data structure and munge it on your own. That's a no-no in object oriented design.

You should always treat the data structure is a black box. You should not be peaking through the windows just because Perl doesn't provide blinds for marking the structure and methods as private. It's still bad manners.

However, Data::Dumper didn't show the name of the class, so it's not an class object. Ogle away at the data structure.

Take a look at the Perl Reference Tutorial to see if that helps clarify the situation for you.

David W.
  • 98,713
  • 36
  • 205
  • 318
0

I didn't get exactly what you mean by "any KEY that is a HASH" - probably you mean: "any VALUE that is a HASH". Anyway, if I got it right, maybe this script might help. It will recursively check the hashes inside the data structure and change the values as needed.

#!/usr/bin/env perl
use strict;
use warnings;

use Data::Dumper;

# test data given in the post
my $VAR1 = {...}; # truncated

sub recursive_change {
    my $hash = shift;
    foreach my $key (keys %{$hash}) {
        my $value = $hash->{$key};
        if ($key eq 'null' && $value eq '1') { # or 1, depends
            $hash->{$key} = '0'; # or 0, depends
        }
        elsif (ref($value) eq 'HASH') {
            recursive_change($value);
        }
    }
}


foreach my $elem (@{$VAR1->{results}}) {
    recursive_change($elem);
}

print Data::Dumper->new([ $VAR1 ],[ '*VAR1' ])->Sortkeys(1)->Dump();

EDIT: setting the whole hash to 0:

sub recursive_change {
    my $hash = shift;
    foreach my $key (keys %{$hash}) {
        my $value = $hash->{$key};
        if (ref($value) eq 'HASH') {
            if ($value->{null} eq '1') {
                $hash->{$key} = 0;
            }
            else {
                change($value);
            }
        }
    }
}

while not the best solution, it works.

Karsten S.
  • 2,165
  • 14
  • 27
  • `print Data::Dumper->Dump([$results]), "\n";` That is how I am seeing the structure of the data that is being returned. The solution you provided requires me to explicitly define $VAR1 and it fails – narddawg314 Mar 20 '14 at 20:52
  • ok, I just copied your output to my script. So you can write `foreach my $elem (@{$results})` if `$results` is an ARRAYREF or `foreach my $elem (@{$results->{results}})` if it is a HASHREF containing the `results` key. – Karsten S. Mar 20 '14 at 20:57
  • ok, this is excellent, and I'm getting closer. What is happening now is the value of 'null' is being set to 0. What I'm actually needing is to change that key/value pair to '0'. Not just the value..does that make sense? Basically, I don't want another nested hash there and want to see a uniform response sent back. – narddawg314 Mar 20 '14 at 21:33
  • ok - works with the second `recursive_change` function. Just exchange it. – Karsten S. Mar 20 '14 at 21:47
  • Swapped the if and the elsif actions and that seems to work! thank you very much!! – narddawg314 Mar 20 '14 at 22:07
0

It sounds like you need to iterate through the elements of results, and for each of them, if any of the values is a hashref, and that hashref has the key-value pair null => 1, replace it with null => 0.

That should look something like:

# iterate through results
for my $result (@$results) {
    # check each hash in results for nested hashes
    for my $key (keys(%$result)) {
        # if the value here is a hashref, do stuff
        if(ref $result->{$key} eq 'HASH') {
            # the stuff to be done 
            # specifically, replace null => 1 with null => 0
            $result->{$key}->{null} = 0 if $result->{$key}->{null} == 1;
        }
    }
}

It appears to do what you want (replace 4 instances of null=>1 with null=>0) when I tested it. I'm sure there's a prettier way to write this.

dorr
  • 796
  • 1
  • 5
  • 11