2010-09-24 88 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