然后按子键排序键 - 如果子键undef设置为空字符串

时间:2010-07-08 18:51:40

标签: perl sorting hash perl-data-structures

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 个答案:

答案 0 :(得分:3)

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";
    }
}

答案 1 :(得分:3)

Schwartzian transform怎么样?

#! /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;

请记住从后到前阅读Schwartzian变换。第一个最接近结尾 - map将某个未指定顺序的记录列表展平或“非规范化”%data。嵌套map是到达子键所必需的。要处理任意深度嵌套,请递归定义{{​​1}}。

我们做了一个早期的传递来收集所有使用的子密钥,因此如果没有特定的子密钥,flatten的值是未定义的值。使用$data{$key}{$_},版本5.10.0中的defined-or new new指定默认值//

使用格式的记录

""

排序很简单:比较各自的第一个元素(键),如果它们相等,则回退到秒(子键)。

最后,最外面的[ "KEY1", "SUBKEY3", "75.00" ], [ "KEY1", "SUBKEY1", "Canada" ], ... 格式化现在排序的非规范化记录以进行输出,结果列表通过map运算符转到标准输出。

输出:

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 ""

要使用每个键的相应键将同一行的子键分组,请使用

等代码
print

你可以用功能风格来编写它,但结果却是一团糟:

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";
}

输出:

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 ""

答案 2 :(得分:2)

我假设提前知道了一组子项。

#!/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