Arrays 如何从Perl哈希中删除空数组/引用?

Arrays 如何从Perl哈希中删除空数组/引用?,arrays,perl,hash,Arrays,Perl,Hash,假设我有以下Perl哈希: %hash = ( 'A' => { 'B' => ['C', 'D', 'E'], 'F' => { 'G' => [], 'H' => [] }, 'I' => [] } ); 我想去掉[],得到下面的散列结果: %hash = ( 'A' => [ 'B' => ['C', 'D', 'E'],

假设我有以下Perl哈希:

%hash = ( 
    'A' => { 
        'B' => ['C', 'D', 'E'], 
        'F' => { 'G' => [], 'H' => [] }, 
        'I' => [] 
        } );
我想去掉
[]
,得到下面的散列结果:

%hash = ( 
    'A' => [ 
       'B' => ['C', 'D', 'E'], 
       'F' => [ 'G', 'H', 'I' ] 
        ] 
    )

(我希望我的
{}
[]
得到了平衡,如果没有,我深表歉意,但是)基本上我想让它不存在空数组/ref。我确信这是可能的/简单的,但我不确定
delete()
是否有效,或者是否有更好的方法或Perl模块。有人能把我引向正确的方向吗?

看起来您的数据可能是任意嵌套的,您希望递归地遍历它,将某些模式重写为其他模式。为此,我将使用
Data::Visitor

use Data::Visitor::Callback;
use List::MoreUtils 'all';

my $visitor = Data::Visitor::Callback->new(
    hash => sub {
        my ($self, $href) = @_;

        # fold hashrefs with only empty arrayrefs as values into arrayrefs
        if (all { ref $_ eq 'ARRAY' && !@{ $_ } } values %{ $href }) {
            return [ keys %{ $href } ];
        }

        # strip k/v pairs with an empty arrayref as a value
        return {
            map {
                $_ => $href->{$_}
            } grep {
                ref $href->{$_} ne 'ARRAY' || @{ $href->{$_} }
            } keys %{ $href }
        };
    },
);

my %new_hash = %{ $visitor->visit(\%hash) };

这只是说明了我将使用的基本方法,并且恰好适用于您提供的示例输入。它可能需要各种调整,这取决于您在其他注释中指出的角落案例中想要做什么。

[这应该是一条注释,但我需要格式。]

你的问题令人费解。(1) 根据什么原则,
I
键(来自原始散列)会在
F
键的列表中结束(在预期散列中)?(2) 如果
F
包含空数组引用之外的内容(请参见我对原始哈希的添加),会发生什么

遍历散列(树,无论什么)是任何程序员都应该知道的技术,但在某些情况下,我认为治疗几乎比疾病更糟糕

您的预期输出是否符合您的预期?你在课文中所说的似乎不同。我在我的例子中使用了他的哈希

如果你使用队列,这很容易。从顶级散列开始。每次遇到哈希引用时,都会将其添加到队列中。当您遇到数组ref时,检查它是否有值,如果没有,则删除该键。你留下的一切:

#!perl
use strict;
use warnings;
use 5.010;

my %hash = ( # From FM
    'A' => {
        'B' => ['C', 'D', 'E'],
        'F' => {
            'G' => [],
            'H' => [],
            'Z' => ['FOO', 'BAR'],  # Not in the OP's original.
        },
        'I' => [],
    },
);

my @queue = ( \%hash );

while( my $ref = shift @queue ) {
     next unless ref $ref eq ref {};

     KEY: foreach my $key ( keys %$ref ) {
        if( ref $ref->{$key} eq ref {} ) {
            push @queue, $ref->{$key};
            next KEY;
            }
        elsif( ref $ref->{$key} eq ref [] ) {
            delete $ref->{$key} if @{$ref->{$key}} == 0;
            }
        }
     }

use Data::Dumper;
print Dumper( \%hash );
我的输出是:

$VAR1 = {
          'A' => {
                   'F' => {
                            'Z' => [
                                     'FOO',
                                     'BAR'
                                   ]
                          },
                   'B' => [
                            'C',
                            'D',
                            'E'
                          ]
                 }
        };

该输出听起来更像您所要求的,而不是您指定的重组。您能澄清输出吗?

是否打算让
I
完全消失,而
G
H
只是折叠成一个列表?您可以将“F”从所有值都是空arrayrefs的hashref转换为arrayref。如果“F”有一个非空值,比如
'F'=>{G=>[],H=>[],J=>'K'}
,会怎么样?对不起,它没有在a和F下保留G,H和I,但是谢谢你的回答。你能解释一下保存这些元素的规则吗?这并不难做到,但我总是担心从一个单独的例子工作。谢谢rafl!这正是我需要的!
$VAR1 = {
          'A' => {
                   'F' => {
                            'Z' => [
                                     'FOO',
                                     'BAR'
                                   ]
                          },
                   'B' => [
                            'C',
                            'D',
                            'E'
                          ]
                 }
        };