Question

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'
    }
  ]
};
Was it helpful?

Solution 2

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.

OTHER TIPS

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top