perl - YAML 与自定义方法合并
问题描述
我正在尝试合并两个 yml:-
use Hash::Merge qw( merge );
use YAML;
$file1 = a.yml
$file2 = b.yml
my $load1=&YAML::LoadFile($file1);
my $load2=&YAML::LoadFile($file2);
my $merge_data = merge($load1, $load2);
my $out_yml = Dump $final_soc_cfg_request;
我在自定义合并机制中遗漏了什么吗?
解决方案
对于问题中显示的数据,这是使用Hash::Merge::add_behavior_spec的一种方法。
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd pp);
use Storable qw(dclone);
use YAML qw(LoadFile);
use Hash::Merge qw(merge);
die "Usage: $0 file1 file2\n" if @ARGV != 2;
my ($fname1, $fname2) = @ARGV;
my $yml1 = LoadFile($fname1);
my $yml2 = LoadFile($fname2);
# Naive merge doesn't do all that's wanted
#my $r = merge($yml1, $yml2); dd $r;
Hash::Merge::add_behavior_spec( {
'SCALAR' => {
'SCALAR' => sub { $_[0] },
'ARRAY' => sub { $_[0] },
'HASH' => sub { $_[0] },
},
'ARRAY' => {
'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] },
'ARRAY' => sub {
# For each pair of hashrefs check whether three (of four) keys
# have the same values; if so, merge them. If not then add them.
my @res;
# Scan: Which elements in each array to merge, and which with which
my (%m1, %m2, %merge);
my @a1 = @{$_[0]};
my @a2 = @{$_[1]};
I1: foreach my $i1 (0..$#a1) {
I2: foreach my $i2 (0..$#a2) {
next if exists $m1{$i1} or exists $m2{$i2};
if (ref $a1[$i1] eq 'HASH' and ref $a2[$i2] eq 'HASH') {
for (qw(name project source)) {
next I2 if $a1[$i1]->{$_} ne $a2[$i2]->{$_};
}
}
# Three key-pairs are same so fourth ones need be merged
$m1{$i1} = $m2{$i2} = 1;
$merge{$i1} = $i2;
}
}
# Now assemble/merge components as marked above
my (%added_1, %added_2); # more bookkeeping needed :(
A1: foreach my $i1 (0..$#a1) {
A2: foreach my $i2 (0..$#a2) {
next A2 if (exists $m1{$i1} and exists $m2{$i2})
or exists $added_2{$i2};
push @res, dclone $a2[$i2];
$added_2{$i2} = 1;
}
next A1 if exists $m1{$i1} or exists $added_1{$i1};
push @res, dclone $a1[$i1];
$added_1{$i1} = 1;
}
foreach my $i (keys %merge) {
push @res,
Hash::Merge::_merge_hashes($a1[$i], $a2[$merge{$i}]);
}
\@res;
},
'HASH' => sub { [ @{ $_[0] }, values %{ $_[1] } ] },
},
'HASH' => {
'SCALAR' => sub { $_[0] },
'ARRAY' => sub { $_[0] },
'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) },
},
}, 'Merge Arrayrefs Recursively As Well',);
my $res = merge($yml1, $yml2);
dd $res;
每个要合并的数据结构都是一个包含 hashref 的 arrayref。当它们被模块合并时,hashrefs 只是集中到一个arrayref 中,而可以想象该arrayref 中的一些hashrefs 可以进一步合并。†</sup>
这在上面的ARRAY
-to-ARRAY
规则中得到了补救,其中检查了 hashrefs 对是否应该合并,如果合并则标记。合并的标准如下。
每个 hahref 都有三个带有字符串值的键,另一个带有 arrayref 值。如果这三个键值对相同,则 hashrefs 应该与三个键值对合二为一,而它们的第四个键的 arrayrefs 应该合并为一个,即该键的值。
然后在第二遍中,那些被标记为要合并的那些将接受_merge_hashes
例程,而其他则被添加到结果数组中。
这打印
{ 工具指针 => [ { 名称 => "tool_gen.config", 项目 => "TOT", 来源=>“等”, 工具 => [{ name => "vipcat" }, { name => "log" }], }, { 名称 => "tool_log.config", 项目 => "TOT", 来源=>“等”, 工具 => [ {名称=>“xc”}, {名称=>“测试”}, {名称=>“vr”}, {名称=>“arbgen2”}, ], }, ], }
我使用Data::Dump来显示复杂的数据结构,因为它默认简单和简洁。(我的选择;当然还有其他的,核心是Data::Dumper,所以已经安装了。)
这只是一个演示,因为我已经简化了一些事情,还使用了固定的硬编码键名列表,以使其适用于显示的数据集。请填写您的真实数据的详细信息。
请注意,必须为每个单独的数据集重新编码,因此必须针对输入数据的任何变化进行审查,并可能对其进行调整或显着更改。
†</sup> 不怪模块。无法分析数组元素是否或如何合并它们,因为没有通用标准。因此,它们被简单地添加到结果数组中,对于任何更具体的需求,都有add_behavior_spec
.
推荐阅读
- javascript - 设置颜色属性时未应用 Vuetify 活动类
- amazon-web-services - 如何根据上次修改时间从 java sdk 获取 aws s3 对象元数据
- node.js - Heroku 上的“请求超时”
- python - 尝试在 Python 中解压缩远程内容时无法将 str 连接到字节
- javascript - 为什么我的两个幻灯片之间有很大的空间?
- javascript - 读取 Json 数组
- scala - 使用 spark scala 中的其他数据帧过滤一个数据帧
- matlab - 如何在 MATLAB 中限制输入字符串的长度、符号、字符和数字
- css - 如何在引导轮播下使缩略图可滚动?
- python - 有没有办法在 Python 中创建一个包含两个 for 的列表推导?