如何在功能上将嵌套哈希转换为记录列表?
How to functionally convert a nested hash to a list of records?
假设我有一个描述货币数量的嵌套哈希:
my %money = (coins => {'50c' => 4}, notes => {'10' => 1, '20' => 5});
我想要的格式是记录列表:
my @money = [
(:type('coins'), :subtype('50c'), value => 4),
(:type('notes'), :subtype('10'), value => 1),
(:type('notes'), :subtype('20'), value => 5),
];
最明显的答案是循环:
my @money;
for %money.kv -> $type, %sub-records {
for %sub-records.kv -> $subtype, $value {
@money.push: (:$type, :$subtype, :$value);
}
}
但我对将变量与填充它的代码分开很反感。接下来,我尝试在输入哈希上创建具有函数转换的变量:
%money.kv.map: -> $k1, %hsh2 { :type($k1) X, %hsh2.kv.map(->$k2, $v2 {:subtype($k2), :$v2, :value($v2)}) }
但是我没有正确嵌套。我想要一个平面列表列表。另外,上面的内容读起来一塌糊涂
妥协是 gather
/take
构造,它让我可以通过迭代构造一个列表,而主范围内没有任何 temporary/uninitialized 垃圾:
my @money = gather for %money.kv -> $type, %sub-records {
for %sub-records.kv -> $subtype, $value {
take (:$type, :$subtype, :$value);
}
};
但我很好奇,仅通过 map
、X
或 Z
和 flat
等列表转换,正确的方法是什么? ("key1"、"key2" 和 "value" 是很好的字段名称,因为算法不应该是特定于域的。)
编辑:我应该提一下,在 Perl 6 中,gather
/take
是最易读的解决方案(最适合非只写的代码)。我仍然对纯功能解决方案感到好奇。
my @money = %money.map:
-> ( :key($type), :value(%records) ) {
slip
:$type xx *
Z
( 'subtype' X=> %records.keys )
Z
( 'value' X=> %records.values )
}
你可以.kv.map: -> $type, %records {…}
-> ( :key($type), :value(%records) ) {…}
解构 Pair 对象
:$type
创建 type => $type
对
:$type xx *
无限重复 :$type
(Z
在任何输入停止时停止)
('subtype' X=> %records.keys)
创建配对列表
(注意.keys
和.values
如果不修改调用之间的Hash,顺序是一样的)
Z
压缩两个列表
slip
导致序列的元素滑入外层序列
(flat
会压扁太多)
如果您希望对它们进行排序
my @money = %money.sort.map: # 'coins' sorts before 'notes'
-> ( :key($type), :value(%records) ) {
# sort by the numeric part of the key
my @sorted = %records.sort( +*.key.match(/^\d+/) );
slip
:$type xx *
Z
( 'subtype' X=> @sorted».key )
Z
( 'value' X=> @sorted».value )
}
你可以做到 .sort».kv.map: -> ($type, %records) {…}
假设我有一个描述货币数量的嵌套哈希:
my %money = (coins => {'50c' => 4}, notes => {'10' => 1, '20' => 5});
我想要的格式是记录列表:
my @money = [
(:type('coins'), :subtype('50c'), value => 4),
(:type('notes'), :subtype('10'), value => 1),
(:type('notes'), :subtype('20'), value => 5),
];
最明显的答案是循环:
my @money;
for %money.kv -> $type, %sub-records {
for %sub-records.kv -> $subtype, $value {
@money.push: (:$type, :$subtype, :$value);
}
}
但我对将变量与填充它的代码分开很反感。接下来,我尝试在输入哈希上创建具有函数转换的变量:
%money.kv.map: -> $k1, %hsh2 { :type($k1) X, %hsh2.kv.map(->$k2, $v2 {:subtype($k2), :$v2, :value($v2)}) }
但是我没有正确嵌套。我想要一个平面列表列表。另外,上面的内容读起来一塌糊涂
妥协是 gather
/take
构造,它让我可以通过迭代构造一个列表,而主范围内没有任何 temporary/uninitialized 垃圾:
my @money = gather for %money.kv -> $type, %sub-records {
for %sub-records.kv -> $subtype, $value {
take (:$type, :$subtype, :$value);
}
};
但我很好奇,仅通过 map
、X
或 Z
和 flat
等列表转换,正确的方法是什么? ("key1"、"key2" 和 "value" 是很好的字段名称,因为算法不应该是特定于域的。)
编辑:我应该提一下,在 Perl 6 中,gather
/take
是最易读的解决方案(最适合非只写的代码)。我仍然对纯功能解决方案感到好奇。
my @money = %money.map:
-> ( :key($type), :value(%records) ) {
slip
:$type xx *
Z
( 'subtype' X=> %records.keys )
Z
( 'value' X=> %records.values )
}
你可以.kv.map: -> $type, %records {…}
-> ( :key($type), :value(%records) ) {…}
解构 Pair 对象:$type
创建type => $type
对:$type xx *
无限重复:$type
(Z
在任何输入停止时停止)('subtype' X=> %records.keys)
创建配对列表
(注意.keys
和.values
如果不修改调用之间的Hash,顺序是一样的)Z
压缩两个列表slip
导致序列的元素滑入外层序列
(flat
会压扁太多)
如果您希望对它们进行排序
my @money = %money.sort.map: # 'coins' sorts before 'notes'
-> ( :key($type), :value(%records) ) {
# sort by the numeric part of the key
my @sorted = %records.sort( +*.key.match(/^\d+/) );
slip
:$type xx *
Z
( 'subtype' X=> @sorted».key )
Z
( 'value' X=> @sorted».value )
}
你可以做到 .sort».kv.map: -> ($type, %records) {…}