Perl вложенные структуры данных

У меня проблемы с обертыванием мозга вокруг возвращаемой структуры данных... Что мне нужно сделать, это проверить результаты и изменить поле, если это HASH. В пределах "результатов" любой КЛЮЧ, который является ХЭШОМ с КЛЮЧОМ "ноль" и значением "1", должен быть изменен на "0". Ниже я вставил некоторые данные из Data::Dumper возврата. В этом случае я хочу изменить данные в четырех разных местах. Я имел дело с этим в течение некоторого времени и просто не могу понять это... любая помощь приветствуется.

$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'
    }
  ]
};

3 ответа

Решение

Я не совсем понял, что вы имеете в виду под "любым КЛЮЧОМ, который является ХЭШОМ" - возможно, вы имеете в виду: "любым ЗНАЧЕНИЕМ, КОТОРОМ ХЭШ" В любом случае, если я правильно понял, возможно, этот скрипт может помочь. Он будет рекурсивно проверять хэши внутри структуры данных и изменять значения по мере необходимости.

#!/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();

РЕДАКТИРОВАТЬ: установка всего хэша в 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);
            }
        }
    }
}

пока не лучшее решение, оно работает.

У меня проблемы с обертыванием мозга вокруг возвращаемой структуры данных...

Вы уже приняли ответ, я просто собираюсь уточнить толкование Data::Dumper выход:

  • каждый {...} означает ссылку на хеш. Вот увидишь key => value, как хеш-элементы.
  • каждый [...] представляет ссылку на массив. Вот увидишь value, как элементы массива.

Разбивая то, что у вас есть:

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

Или $VAR->{results} = [];

Это хеш с одним ключом results, Хэш имеет ссылку на массив в качестве значения. До сих пор:

$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
    }

В этом массиве есть два значения, каждое из которых указывает на ссылку на хеш:

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

В каждом из этих массивов ссылка на хэш имеет 12 ключей и значений:

  • админ
  • поток данных
  • devices_online
  • объект
  • ifDescr
  • ifDevice
  • IfIndex
  • опер
  • percent_online
  • карта
  • Это ссылки...
    • total_devices
    • devices_offline

Первые 10 - это просто пары ключ / значение. Последние два являются ссылками на дополнительный хеш с одной парой ключ / значение. Ключ нулевой. Я предполагаю, что это какая-то ошибка.

Теперь я могу сослаться на один из следующих пунктов:

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

Предполагая текущую структуру, вот способ обратиться к ней в цикле:

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};
      }
   }
}

Это не будет полностью работать, потому что total_device а также device_offline опять хеш ссылки. Я должен сделать исключение в моем самом внутреннем цикле, и если любой из них является ключом к моему внутреннему хешу, мне нужно сделать еще одно разыменование, чтобы получить хеш. Я позволю тебе решить это.

Конечно, я знаю свою структуру, поэтому я мог написать программную структуру для ее обработки. Если бы я не знал структуру моей структуры данных, мне пришлось бы использовать ref Команда, чтобы выяснить, имею ли я в виду хеш или массив, и разыменование и цикл соответственно. Это в значительной степени то, что Data::Dumper делает.

Я обычно предполагаю, что такая сложная структура происходит из конструктора класса, и я ожидаю увидеть благословенное имя класса объекта в таком дампе Data::Dumper. В этом случае я бы сказал вам использовать методы для этого класса, а не деконструировать структуру данных и разбирать ее самостоятельно. Это нет-нет в объектно-ориентированном дизайне.

Вы всегда должны относиться к структуре данных как черный ящик. Вы не должны проходить через окна только потому, что Perl не предоставляет жалюзи для маркировки структуры и методов как приватных. Это все еще плохие манеры.

Однако Data::Dumper не показывал имя класса, поэтому это не объект класса. Оглянись на структуру данных.

Посмотрите справочное руководство по Perl, чтобы понять, поможет ли это вам разобраться.

Похоже, вам нужно перебирать элементы результатов, и для каждого из них, если какое-либо из значений является хеш-рефом и этот хеш-реф имеет пару ключ-значение null => 1, замените его на null => 0.

Это должно выглядеть примерно так:

# 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;
        }
    }
}

Похоже, что я делаю, что вы хотите (замените 4 экземпляра null => 1 на null=>0), когда я протестировал это. Я уверен, что есть более красивый способ написать это.

Другие вопросы по тегам