2010-09-24 89 views
2

可以说我有下面的Perl哈希:如何从Perl哈希中删除空的数组/参考?

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

,我想摆脱[]的来获得下面的散列结果:

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

(我希望我我的{}[]平衡,我的道歉,如果没有,但)基本上我想这样做,没有空阵列/裁判存在。我确信这是可能的/简单的,但我不确定delete()是否可以工作,或者是否有更好的方法或Perl模块。有人能引导我走向正确的方向吗?

+2

它是否意味着'I'完全消失,但'G'和'H'只是折叠成一个列表? – daxim 2010-09-24 21:26:30

+1

您将'F'从其中所有值为空arrayref的hashref转换为arrayref。如果'F'有非空值,如'F'=> {G => [],H => [],J =>'K'}'? – mob 2010-09-24 21:30:15

回答

5

看起来好像你的数据可能是任意嵌套的,你想递归地遍历它,将某些模式重写到其他模式。为此,我会使用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' && [email protected]{ $_ } } 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) }; 

这只是说明我会使用的基本方法,以及发生在你给的例子输入工作。它可能需要根据你想在其他评论中指出的角落案例中做什么进行各种调整。

+0

谢谢!这正是我需要的! – Nick 2010-09-26 17:03:49

1

[这应该是一个评论,但我需要的格式。]

你的问题是令人费解。 (1)I密钥(来自原始散列)以什么原理结束于F密钥的列表中(在预期的散列中)? (2)如果F包含除了空数组参考之外的东西会发生什么(请参阅我对原始哈希的补充)?

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

my %hash_expected = (
    'A' => [ 
     'B' => ['C', 'D', 'E'], 
     'F' => [ 'G', 'H', 'I'], # Where should the Z info go? 
    ], 
); 
1

散步(树,无论)是一种技术,任何程序员应该知道。 rafl uses a visitor module,但在某些情况下,我认为治疗几乎比疾病更糟糕。

您的预期输出是你的意图吗?这与你在文中所说的看起来不同,如FM says。我在我的例子中使用他的散列。

如果您使用队列,这很容易。你从顶层散列开始。每次遇到散列引用时,都会将其添加到队列中。当你运行一个数组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' 
          ] 
       } 
     }; 

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

+0

+1。该行'my @queue =(\%hash)'非常巧妙。学到了一些东西...... – dawg 2010-09-25 18:32:20

+0

对不起,它没有在A和F下保存G,H和我,但是谢谢你的回答 – Nick 2010-09-26 17:02:08

+0

你能否阐述保留这些元素的规则?这并不难,但我总是担心从一个单独的例子开始工作。 – 2010-09-26 20:59:35