затем сортировать ключ по подразделу - если для подключа undef установлено значение null - PullRequest
2 голосов
/ 08 июля 2010
my %data (
    KEY1 => {
        SUBKEY1 => "Canada",
        SUBKEY3 => "75.00",
        SUBKEY2 => "50.00",    
    },
    KEY3 => {
        SUBKEY2 => "150.00",
    },  
    KEY2 => { 
        SUBKEY3 => "200.00",
        SUBKEY1 => "Mexico",
 },
);

Как распечатать список, отсортированный по имени Keyname и для каждого имени ключа, отсортированный по имени?

Вот что я хочу напечатать: (обратите внимание, что если подраздел не определен,заполнитель для подключа с пустой строкой присутствует)

KEY1: SUBKEY1 is "Canada"
KEY1: SUBKEY2 is "50.00"
KEY1: SUBKEY3 is "75.00"
KEY2: SUBKEY1 is ''
KEY2: SUBKEY2 is "150.00"
KEY2: SUBKEY3 is ''
KEY3: SUBKEY1 is "Mexico"
KEY3: SUBKEY2 is ''
KEY3: SUBKEY3 is "200.00"

Ответы [ 3 ]

3 голосов
/ 08 июля 2010

Как насчет преобразования Шварца ?

#! /usr/bin/perl

use 5.10.0;  # for // (aka defined-or)
use warnings;
use strict;

my %data = ...;

# get all subkeys used in %data
my @subkeys = keys %{
  { map { map +($_ => 1),
          keys %{ $data{$_} } }
    keys %data
  }
};

print map qq|$_->[0]: $_->[1] is "$_->[2]"\n|,
      sort { $a->[0] cmp $b->[0]
                     ||
             $a->[1] cmp $b->[1] }
      map { my $key = $_;
            map [ $key, $_, $data{$key}{$_} // "" ] =>
            @subkeys }
      keys %data;

Не забудьте прочитать преобразования Шварцяна сзади-вперед. Первый - ближайший к концу - map сглаживает или «денормализует» %data в список записей в некотором неопределенном порядке. Вложенные map необходимы для доступа к подразделам. Для обработки сколь угодно глубокой вложенности определите рекурсивно flatten.

Мы сделали более раннюю передачу, чтобы собрать все используемые подключи, поэтому, если конкретный подраздел отсутствует, значение $data{$key}{$_} является неопределенным значением. Используя //, определенный или новый оператор, новый в версии 5.10.0, задается значение по умолчанию "".

со сплюснутыми записями вида

[ "KEY1", "SUBKEY3", "75.00" ],
[ "KEY1", "SUBKEY1", "Canada" ],
...

сортировка проста: сравните соответствующие первые элементы (ключи) и, если они равны, вернитесь к секундам (подразделы).

Наконец, самый внешний map форматирует теперь отсортированные денормализованные записи для вывода, и результирующий список отправляется на стандартный вывод с помощью оператора print.

Выход:

KEY1: SUBKEY1 is "Canada"
KEY1: SUBKEY2 is "50.00"
KEY1: SUBKEY3 is "75.00"
KEY2: SUBKEY1 is "Mexico"
KEY2: SUBKEY2 is ""
KEY2: SUBKEY3 is "200.00"
KEY3: SUBKEY1 is ""
KEY3: SUBKEY2 is "150.00"
KEY3: SUBKEY3 is ""

Чтобы сгруппировать подключи в одной строке с соответствующим ключом каждого, используйте код, например

my @subkeys = sort keys %{ ... ;

foreach my $key (sort keys %data) {
  my @values;
  foreach my $subkey (@subkeys) {
    my $value = $data{$key}{$subkey} // "";
    push @values => qq|$subkey is "$value"|;
  }

  local $" = ", ";
  print "$key: @values\n";
}

Вы можете написать это в функциональном стиле, но в результате получается грязный беспорядок:

print map { my $key = $_;
            "$key: " .
              join(", " =>
                map { my $value = $data{$key}{$_} // "";
                      qq|$_ is "$value"|
                    }
                @subkeys) .
            "\n"
          }
      sort keys %data;

Выход:

KEY1: SUBKEY1 is "Canada", SUBKEY2 is "50.00", SUBKEY3 is "75.00"
KEY2: SUBKEY1 is "Mexico", SUBKEY2 is "", SUBKEY3 is "200.00"
KEY3: SUBKEY1 is "", SUBKEY2 is "150.00", SUBKEY3 is ""
3 голосов
/ 08 июля 2010
use strict;
use warnings;

my %data = (
    KEY1 => {
        SUBKEY1 => "Canada",
        SUBKEY3 => "75.00",
        SUBKEY2 => "50.00",    
    },
    KEY3 => {
        SUBKEY2 => "150.00",
    },  
    KEY2 => { 
        SUBKEY3 => "200.00",
        SUBKEY1 => "Mexico",
 },
);

my %all_sub_keys;
for my $sub_hash (values %data){
    $all_sub_keys{$_} ++ for keys %$sub_hash;
}

my @all_sub_keys = sort keys %all_sub_keys;

for my $k ( sort keys %data ){
    for my $sk (@all_sub_keys){
        my $val = exists $data{$k}{$sk} ? $data{$k}{$sk} : '--';
        print join(' ', $k, $sk, $val), "\n";
    }
}
2 голосов
/ 08 июля 2010

Я предполагаю, что набор подразделов известен заранее.

#!/usr/bin/perl

use strict; use warnings;

my %data = (
    KEY1 => {
        SUBKEY1 => "Canada",
        SUBKEY3 => "75.00",
        SUBKEY2 => "50.00",
    },
    KEY3 => {
        SUBKEY2 => "150.00",
    },
    KEY2 => {
        SUBKEY3 => "200.00",
        SUBKEY1 => "Mexico",
 },
);

my @subkeys = qw( SUBKEY1 SUBKEY2 SUBKEY3 );

for my $key ( sort keys %data ) {
    my %sub = map {
        my $v = $data{$key}{$_};
        $_ => defined($v) ? $v : '';
    } @subkeys;

    for my $subkey ( @subkeys ) {
        print "$key $subkey $sub{$subkey}\n";
    }
}

Выход:

KEY1 SUBKEY1 Canada
KEY1 SUBKEY2 50.00
KEY1 SUBKEY3 75.00
KEY2 SUBKEY1 Mexico
KEY2 SUBKEY2
KEY2 SUBKEY3 200.00
KEY3 SUBKEY1
KEY3 SUBKEY2 150.00
KEY3 SUBKEY3
...