使用Perl Hash::Merge模块合并YAML文件时重复项未被处理的问题咨询
问题:Hash::Merge合并YAML后出现重复条目怎么办?
先贴一下你的代码,方便梳理问题:
use strict; use warnings; use Hash::Merge qw( merge ); Hash::Merge::set_behavior('RETAINMENT_PRECEDENT'); use File::Slurp qw(write_file); use YAML; my $yaml1 = $ARGV[0]; my $yaml2 = $ARGV[1]; my $yaml_output = $ARGV[2]; my $clkgrps = YAML::LoadFile($yaml1); my $clkgrps1 = YAML::LoadFile($yaml2); my $clockgroups = merge($clkgrps1, $clkgrps); my $out_yaml = Dump $clockgroups; write_file($yaml_output, { binmode => ':raw' }, $out_yaml);
你提到合并后出现重复条目,原本相同的内容被当成不同项保留了——咱们先拆解原因,再给你针对性的解决办法:
为什么会出现重复?
Hash::Merge的RETAINMENT_PRECEDENT行为,对哈希的处理是保留左侧(第一个参数)的键值,冲突时覆盖右侧,但对数组的默认逻辑是直接把两个数组的元素追加到一起,完全不检查元素是否重复。所以大概率你的YAML里包含数组结构,两个源文件的数组有相同元素,合并后就被全部保留了。
如果是哈希键重复,那可能是加载YAML时键的格式有差异(比如大小写不同、带/不带引号),导致Hash::Merge误判为不同键,这时候得先检查加载后的哈希结构。
解决方法:自定义合并规则去重
1. 针对简单数组元素的去重
如果你的数组里是字符串、数字这类简单类型,可以直接自定义Hash::Merge的数组合并逻辑,合并后自动去重:
use strict; use warnings; use Hash::Merge qw( merge ); use File::Slurp qw(write_file); use YAML; # 自定义带去重的合并规则 Hash::Merge::specify_behavior( { 'SCALAR' => { 'SCALAR' => sub { $_[0] }, # 冲突时保留左侧值 'ARRAY' => sub { [ $_[0], @{$_[1]} ] }, 'HASH' => sub { $_[0] }, }, 'ARRAY' => { 'SCALAR' => sub { [ @{$_[0]}, $_[1] ] }, 'ARRAY' => sub { # 合并数组并去重,只保留首次出现的元素 my %seen; grep { !$seen{$_}++ } (@{$_[0]}, @{$_[1]}); }, 'HASH' => sub { [ @{$_[0]}, $_[1] ] }, }, 'HASH' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { [ $_[0], @{$_[1]} ] }, 'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) }, }, }, 'RETAINMENT_DUPLICATE_FREE' # 给规则起个标识名 ); # 后续代码和你原来的一致 my $yaml1 = $ARGV[0]; my $yaml2 = $ARGV[1]; my $yaml_output = $ARGV[2]; my $clkgrps = YAML::LoadFile($yaml1); my $clkgrps1 = YAML::LoadFile($yaml2); my $clockgroups = merge($clkgrps1, $clkgrps); my $out_yaml = Dump $clockgroups; write_file($yaml_output, { binmode => ':raw' }, $out_yaml);
2. 针对复杂数组元素的去重
如果数组里是哈希、嵌套结构这类复杂元素,上面的简单去重就失效了,得用Data::Compare模块来判断元素是否完全相同:
先安装模块:
cpan Data::Compare
然后修改合并规则里的数组处理部分:
use Data::Compare; # 自定义复杂元素的数组去重规则 Hash::Merge::specify_behavior( { # SCALAR、HASH部分和上面一致 'ARRAY' => { 'SCALAR' => sub { [ @{$_[0]}, $_[1] ] }, 'ARRAY' => sub { my @combined = (@{$_[0]}, @{$_[1]}); my @unique; # 遍历元素,只保留之前未出现过的相同结构项 for my $elem (@combined) { push @unique, $elem unless grep { Compare($_, $elem) } @unique; } return \@unique; }, 'HASH' => sub { [ @{$_[0]}, $_[1] ] }, }, }, 'RETAINMENT_COMPLEX_DUPLICATE_FREE' );
3. 检查哈希键的重复问题
如果是哈希键重复,先打印加载后的哈希看看实际的键是什么:
use Data::Dumper; print Dumper($clkgrps); print Dumper($clkgrps1);
如果发现键是大小写不同(比如Clock和clock),或者有多余引号,那你需要在加载YAML后先统一键的格式,比如把所有键转成小写,再进行合并。
总结
核心思路就是根据你的YAML结构,自定义Hash::Merge的合并规则,让它在合并数组(或处理哈希键)时自动去重。上面的代码可以直接替换你原来的规则部分,试试应该就能解决重复问题了。
内容的提问来源于stack exchange,提问作者Ramse
相关产品推荐
相关产品推荐

