2014-05-08 40 views
-3

我想寫一個函數,它會返回任何級別下面的數據結構中所有「id」值的列表,並按數字排序。同樣,如果在數據結構中的多個位置中找到相同的值,則只應將其包含在返回的列表中一次。如何獲取perl數據結構中的所有鍵值?

sub ids {
   
    my ($data) = @_;
  
   
    # Define this function 

 } 


 
 my $data = {
   
'top' => { 
     'window' => { 
      'elements' => { 
       { id => 44, name => 'link', value => 'www.cnn.com' }, 

     { id => 48, name => 'title', value => 'CNN Home Page' },
  
       { id => 100, name => 'author', value => 'Admin' }
  
      }, 

    id => 19 

   }, 

   'cache' => { 

    { id => 199, data => '5' }, 

    { id => 40, data => '9' },
  
      { id => 100, data => { name => 'author', value => 'Admin' } 
}
   },
  
     id => 55
 }, 

  id => 1 

 }; 

 
 # should print 「1, 19, 40, 44, 49, 55, 100, 199」
  
print join(', ', ids($data)) . 「\n」; 
+1

'$ data'不包含有效的Perl數據結構,例如'elements'的值是什麼? –

回答

1

一些數據結構的應陣列,而不是散列如OP,

use strict; 
use warnings; 

sub ids_r { 
    my ($data) = @_; 

    return map { 
    my $r = ref($data->{$_}); 
    $r eq "HASH" ? ids_r($data->{$_}) : 
     $r   ? map ids_r($_), @{$data->{$_}} : 
     $_ eq "id" ? $data->{$_} : 
    (); 
    } keys %$data; 
} 
sub ids {   
    my ($data) = @_; 
    my %seen; 
    return 
    sort { $a <=> $b } 
    grep !$seen{$_}++, ids_r($data); 
} 
my $data = {   
    'top' => { 
    'window' => { 
     'elements' => [ 
      { id => 44, name => 'link', value => 'www.cnn.com' }, 
      { id => 48, name => 'title', value => 'CNN Home Page' }, 
      { id => 100, name => 'author', value => 'Admin' }  
     ], 
     id => 19 
    }, 
    'cache' => [ 
      { id => 199, data => '5' }, 
      { id => 40, data => '9' },  
      { id => 100, data => { name => 'author', value => 'Admin' } } 
    ],  
    id => 55  
    }, 
    id => 1 
}; 
print join(', ', ids($data)); 

輸出

1, 19, 40, 44, 48, 55, 100, 199 
0

下面是一個簡單的遞歸溶液。很容易看到這裏發生了什麼。

# There is a faster version of `uniq` provided by List::MoreUtils on CPAN. 
sub uniq { 
    my %seen; 
    grep !$seen{$_}++, @_; 
} 

sub ids { 
    my $val = shift; 
    my $ref = ref $val; 
    my @r; 

    if ($ref eq 'HASH') 
    { 
     @r = map ids($_), grep ref, values(%$val); 
     push @r, $val->{id} if exists $val->{id}; 
    } 

    elsif ($ref eq 'ARRAY') 
    { 
     @r = map ids($_), grep ref, @$val; 
    } 

    sort { $a <=> $b } uniq(@r); 
} 

@mpapec提供了使用遞歸沒有做排序(他回答叫ids_r子)類似的解決方案,然後調用從一個單獨的包裝函數(他回答叫ids子),它提供最後的分類。這樣更有效率,但可以說更加複雜。 (事實上​​,因爲他有兩個類似命名的函數,答案的第一個版本包含了一個錯誤,否定了分割排序的好處。)

這是另一種使用基於隊列的方法而不是遞歸的技術。如果您的數據結構非常大,您可能會發現這種方法運行速度更快。

# There is a faster version of `uniq` provided by List::MoreUtils on CPAN. 
sub uniq { 
    my %seen; 
    grep !$seen{$_}++, @_; 
} 

sub ids { 
    my @r; 
    while (@_) { 
     my $val = shift; 
     my $ref = ref($val); 

     if ($ref eq 'HASH') 
     { 
      push @r, $val->{id} if exists $val->{id}; 
      push @_, grep ref, values %$val; 
     } 

     elsif ($ref eq 'ARRAY') 
     { 
      push @_, grep ref, @$val; 
     } 
    } 

    sort { $a <=> $b } uniq(@r); 
} 
+0

也許多種排序不會增加效率。 –

+0

@mpapec - 同樣可以說你的答案。 – tobyink

+0

我正在討論在遞歸函數內使用'sort'。 –

相關問題