2014-09-29 4 views
1

Как получить значения ключей в хеш хэш в Perl? Я попытался использовать функцию клавиш. Я хотел удалить дубликаты, а затем отсортировать их, что я мог бы сделать с использованием функций uniq и sort. Я что-то пропустил?Perl - извлекать значения из хэша хэшей

#!/usr/bin/perl 

use warnings; 
use strict; 

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

    my @allID = keys %{$data}; 

    my @unique = uniq @allID; 
    foreach (@unique) { 
     @allUniqueID = $_; 
    } 

    my @result = sort{$a<=>$b}(@allUniqueId); 
    return @result; 
} 

my $data = { 
    'first' => { 
     'second' => { 
      'third1' => [ 
       { id => 44, name => 'a', value => 'aa' }, 
       { id => 48, name => 'b', value => 'bb' }, 
       { id => 100, name => 'c', value => 'cc' } 
      ], 
      id => 19 
     }, 
     'third2' => [ 
      { id => 199, data => 'dd' }, 
      { id => 40, data => 'ee' }, 
      { id => 100, data => { name => 'f', value => 'ff' } } 
     ], 
     id => 55 
    }, 
    id => 1 
}; 

# should print “1, 19, 40, 44, 48, 55, 100, 199” 
print join(', ', ids($data)) . "\n"; 

Я знаю, что это неполное, но я не уверен, как действовать дальше. Любая помощь будет оценена по достоинству.

ответ

2

Эта процедура рекурсивно проходит структуру данных и вытащить все значения, которые соответствуют хэша ключа id, без сортировки результатов или устранения дубликатов:

sub all_keys { 
    my $obj = shift; 

    if (ref $obj eq 'HASH') { 
     return map { 
      my $value = $obj->{$_}; 
      $_ eq 'id' ? $value : ref $value ? all_keys($value) :(); 
     } keys %$obj; 

    } elsif (ref $obj eq 'ARRAY') { 
     return map all_keys($_), @$obj; 

    } else { 
     return; 
    } 
} 

Чтобы сделать сортировку/устранение, просто Называйте это нравится:

my @ids = sort { $a <=> $b } uniq(all_ids($data)); 

(я предполагаю, что uniq процедура определяется в другом месте.)

+0

Uniq был построен в функции, которую я использовал. – abc

+0

'uniq' находится в [' List :: Util'] (https://metacpan.org/module/List::Util) – Borodin

0

Вот мой v ersion рекурсивного подхода

use warnings; 
use strict; 

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

    my @retval; 
    if (ref $data eq 'HASH') { 
     push @retval, $data->{id} if exists $data->{id}; 
     push @retval, ids($_) for values %$data; 
    } 
    elsif (ref $data eq 'ARRAY') { 
     push @retval, ids($_) for @$data; 
    } 
    @retval; 
} 

my $data = { 
    'first' => { 
     'second' => { 
      'third1' => [ 
       { id => 44, name => 'a', value => 'aa' }, 
       { id => 48, name => 'b', value => 'bb' }, 
       { id => 100, name => 'c', value => 'cc' } 
      ], 
      id => 19 
     }, 
     'third2' => [ 
      { id => 199, data => 'dd' }, 
      { id => 40, data => 'ee' }, 
      { id => 100, data => { name => 'f', value => 'ff' } } 
     ], 
     id => 55 
    }, 
    id => 1 
}; 

my @ids = sort { $a <=> $b } ids($data); 
print join(', ', @ids), "\n"; 

выход

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

Обновление

Большой кусок кода в указанном выше растворе там, чтобы работать, как извлечь список значений из ссылки на данные. В последних версиях Perl есть экспериментальная установка, которая позволяет вам использовать оператор values как для хэшей, так и для массивов, а также для ссылок ro, поэтому, если вы используете версию 14 или более поздней версии Perl 5 и можете комфортно отключать экспериментальные предупреждения , то вы можете написать ids вроде этого вместо

use warnings; 
use strict; 
use 5.014; 

sub ids { 
    my ($data) = @_; 
    return unless my $type = ref $data; 

    no warnings 'experimental'; 
    if ($type eq 'HASH' and exists $data->{id}) { 
     $data->{id}, map ids($_), values $data; 
    } 
    else { 
     map ids($_), values $data; 
    } 
} 

выход идентичен предыдущему решению