【问题标题】:Perl nested data structuresPerl 嵌套数据结构
【发布时间】:2014-04-27 22:43:44
【问题描述】:

我无法将我的大脑包裹在返回的数据结构周围......我需要做的是检查结果,如果它是一个 HASH,则修改一个字段。在“结果”中,任何 KEY 为“null”且值为“1”的 HASH 都需要更改为“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'
    }
  ]
};

【问题讨论】:

  • 您现在有两个不同的答案来解释这个问题。澄清一下,您是否只在结果中哈希的特定级别寻找 null => 1,或者您是否可以在哈希中的哈希中找到 null => 1 ...在结果中的哈希中?即,是否需要递归检查?

标签: perl perl-data-structures hash-reference


【解决方案1】:

我无法理解返回的数据结构...

你已经接受了一个答案,我只是要澄清Data::Dumper输出的解释:

  • 每个{...} 表示对哈希的引用。您将看到 key => value, 作为哈希元素。
  • 每个[...] 代表一个对数组的引用。您将看到 value, 作为数组元素。

分解你所拥有的:

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

或者 $VAR->{结果} = [];

这是一个带有单个键 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 所做的。

我通常假设这样一个复杂的结构来自一个 class 构造函数,我希望在这样的 Data:: 中看到对象的 blessed 类名翻斗车。在这种情况下,我会告诉您使用该类的方法,而不是解构数据结构并自行处理。这是面向对象设计中的禁忌。

您应该始终将数据结构视为一个黑盒子。你不应该仅仅因为 Perl 没有提供 blinds 来将结构和方法标记为 private 就通过窗口达到峰值。还是不礼貌。

但是,Data::Dumper 没有显示类的名称,因此它不是类对象。看看数据结构。

查看Perl Reference Tutorial,看看这是否有助于您了解情况。

【讨论】:

  • 感谢您的解释。这个很“讨厌”
【解决方案2】:

我没有完全理解您所说的“任何作为 HASH 的 KEY”的意思 - 可能您的意思是:“任何作为 HASH 的 VALUE”。 无论如何,如果我做对了,也许这个脚本可能会有所帮助。 它将递归检查数据结构内的哈希值并根据需要更改值。

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

虽然不是最好的解决方案,但它确实有效。

【讨论】:

  • print Data::Dumper->Dump([$results]), "\n"; 这就是我看到返回的数据结构的方式。您提供的解决方案需要我明确定义 $VAR1 并且它失败了
  • 好的,我刚刚将您的输出复制到我的脚本中。因此,如果 $results 是一个 ARRAYREF 或 foreach my $elem (@{$results->{results}}) 如果它是一个包含 results 键的 HASHREF,你可以写 foreach my $elem (@{$results})。
  • 好的,这太棒了,我越来越接近了。现在发生的是“null”的值被设置为 0。我真正需要的是将该键/值对更改为“0”。不仅仅是价值……这有意义吗?基本上,我不希望那里有另一个嵌套哈希,并且希望看到发回的统一响应。
  • ok - 与第二个 recursive_change 函数一起使用。换一个就行了。
  • 交换了 if 和 elsif 动作,这似乎有效!非常感谢!!
【解决方案3】:

听起来您需要遍历结果的元素,并且对于每个结果,如果其中任何一个值是 hashref,并且该 hashref 具有键值对 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;
        }
    }
}

当我测试它时,它似乎做了你想做的事(用 null=>0 替换 4 个 null=>1 实例)。我敢肯定有一个更漂亮的方式来写这个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-12
    • 2021-07-12
    • 2018-03-16
    • 1970-01-01
    • 2022-01-22
    • 2011-09-27
    • 2020-01-31
    • 1970-01-01
    相关资源
    最近更新 更多