sort key then by subkey—if subkey undef set to null string
my %data (
KEY1 => {
开发者_Go百科 SUBKEY1 => "Canada",
SUBKEY3 => "75.00",
SUBKEY2 => "50.00",
},
KEY3 => {
SUBKEY2 => "150.00",
},
KEY2 => {
SUBKEY3 => "200.00",
SUBKEY1 => "Mexico",
},
);
How do I print a list that is sorted by Keyname and for each keyname sorted by subkeyname?
Here is what I want to print: (notice that if the subkey wasn't defined, a placeholder for the subkey with a null string is present)
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"
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";
}
}
How about a 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;
Remember to read Schwartzian transforms from back-to-front. The first—closest to the end—map
flattens or "denormalizes" %data
into a list of records in some unspecified order. The nested map
is necessary to reach the subkeys. To handle arbitrarily deep nesting, define flatten
recursively.
We made an earlier pass to collect all the subkeys used, so if a particular subkey is not present, the value of $data{$key}{$_}
is the undefined value. Using //
, the defined-or operator new in version 5.10.0, specifies a default value of ""
.
With flattened records of the form
[ "KEY1", "SUBKEY3", "75.00" ],
[ "KEY1", "SUBKEY1", "Canada" ],
...
sorting is straightforward: compare the respective first elements (the keys) and if those are equal, fall back to the seconds (the subkeys).
Finally, the outermost map
formats the now-sorted denormalized records for output, and the resulting list goes to the standard output by way of the print
operator.
Output:
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 ""
To group subkeys on the same line with the respective key of each, go with code such as
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";
}
You could write it in a functional style, but the result is a muddy mess:
print map { my $key = $_;
"$key: " .
join(", " =>
map { my $value = $data{$key}{$_} // "";
qq|$_ is "$value"|
}
@subkeys) .
"\n"
}
sort keys %data;
Output:
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 ""
I am assuming the set of subkeys is known ahead of time.
#!/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";
}
}
Output:
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
精彩评论