【问题标题】:Merge Perl Hashrefs and unique合并 Perl Hashrefs 和唯一
【发布时间】:2026-01-31 06:05:01
【问题描述】:

我有两个 perl 哈希,内容如下:

第一:

$VAR1 = {
      'name1' => [
                   'adam',
                   'bob'
                 ],
      'name2' => [
                  'Miller',
                  'Schumacher'
                ]
    };

第二:

$VAR1 = {
      'name1' => [
                   'tina',
                   'jason',
                   'jeff'
                 ],
      'name2' => [
                  'Miller',
                  'Schumacher',
                  'Schmidt'
                ]
    };

如何合并它们以获得以下结构并获得 name2 中的唯一项目?

$VAR1 = {
      'name1' => [
                   'tina',
                   'jason',
                   'jeff',
                   'adam',
                   'bob',
                 ],
      'name2' => [
                  'Miller',
                  'Schumacher',
                  'Schmidt'
                ]
    };

【问题讨论】:

  • 作为建议 - 如果您展示您尝试过的内容,您通常会得到更好的答案和响应。

标签: arrays perl merge unique hashref


【解决方案1】:

Hash::Merge模块的右前行为被定制为在合并数组时统一元素。

use strict;
use Hash::Merge qw/merge :custom/;
use List::MoreUtils qw/uniq/;
use Data::Dumper;

my $href1 = { name1 => [ qw/adam bob/ ],
              name2 => [ qw/Miller Schumacher/ ] };

my $href2 = { name1 => [ qw/tina jason jeff/ ],
              name2 => [ qw/Miller Schumacher Schmidt/ ] };

Hash::Merge::specify_behavior(  {
                        SCALAR => {
                                SCALAR => sub { $_[1] },
                                ARRAY  => sub { [ $_[0], @{$_[1]} ] },
                                HASH   => sub { $_[1] },
                        },
                        ARRAY => {
                                SCALAR => sub { $_[1] },
                                # This returns unique elements from two arrays passed
                                ARRAY  => sub { [ uniq( @{$_[0]}, @{$_[1]}) ] },
                                HASH   => sub { $_[1] }, 
                        },
                        HASH => {
                                SCALAR => sub { $_[1] },
                                ARRAY  => sub { [ values %{$_[0]}, @{$_[1]} ] },
                                HASH   => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) }, 
                        },
                }, 
                'Right precedent + Uniq array', 
        );

my $href3 = merge($href1, $href2);

print Dumper $href3;

产生输出:

$VAR1 = {
          'name2' => [
                       'Miller',
                       'Schumacher',
                       'Schmidt'
                     ],
          'name1' => [
                       'adam',
                       'bob',
                       'tina',
                       'jason',
                       'jeff'
                     ]
        };
【解决方案2】:

您必须遍历 name1, name2 键并从 $VAR2->{$k}$VAR1->{$k} 数组中过滤出重复项,

use strict;
use warnings;

my $VAR1 = {
      'name1' => [ 'adam', 'bob' ],
      'name2' => [ 'Miller', 'Schumacher' ]
};
my $VAR2 = {
      'name1' => [ 'tina', 'jason', 'jeff' ],
      'name2' => [ 'Miller', 'Schumacher', 'Schmidt' ]
};

my %result;
for my $k (keys %$VAR1) {
  my %seen;
  $result{$k} = [
    grep { !$seen{$_}++ } @{ $VAR2->{$k} }, @{ $VAR1->{$k} }
  ];
}

use Data::Dumper;
print Dumper \%result;

输出

$VAR1 = {
      'name2' => [
                   'Miller',
                   'Schumacher',
                   'Schmidt'
                 ],
      'name1' => [
                   'tina',
                   'jason',
                   'jeff',
                   'adam',
                   'bob'
                 ]
    };

【讨论】:

  • 谢谢 - 现在通过 foreach 循环解析这些的正确方法是什么?
【解决方案3】:

如果您的需求非常具体 - 将两个哈希与键 name1name2 合并,那么以下应该可以解决问题:

my $first = {
        name1 => [ qw(adam bob) ],
        name2 => [ qw(Miller Schumacher) ],
    };
my $second = {
        name1 => [ qw(tina jason jeff) ],
        name2 => [ qw(Miller Schumacher Schmidt) ],
    };

my $merged = {
        name1 => [ values %{$first->{name1}}, values %{$second->{name1}} ],
        name2 => [ values %{$first->{name2}}, values %{$second->{name2}} ],
    };

如果密钥不是固定的并且事先不知道,Сухой27 的答案将起作用,至少在结构只有两层深的情况下。 如果它可以更深入,您将需要一个递归解决方案。

【讨论】:

    【解决方案4】:

    这是一种通用解决方案,可以采用任意数量的散列,并处理任何散列中缺少的键

    它遍历要组合的所有散列键的列表,并使用map 将所有散列中每个键的数组值连接起来

    我使用Data::Dump 仅用于显示生成的哈希数据

    use strict;
    use warnings;
    
    use List::MoreUtils qw/ uniq /;
    use Data::Dump;
    
    my %ha = (
        name1 => [ "adam",   "bob" ],
        name2 => [ "Miller", "Schumacher" ],
    );
    
    my %hb = (
        name1 => [ "tina",   "jason",      "jeff" ],
        name2 => [ "Miller", "Schumacher", "Schmidt" ],
    );
    
    my @hashes = \( %ha, %hb );
    
    my %new;
    
    for my $k ( uniq map keys %$_, @hashes ) {
        $new{$k} = [
            uniq map @{ $_->{$k} // [] }, @hashes
        ];
    }
    
    dd \%new;
    

    输出

    {
      name1 => ["adam", "bob", "tina", "jason", "jeff"],
      name2 => ["Miller", "Schumacher", "Schmidt"],
    }
    

    如果出于任何原因您不想安装非核心库模块List::MoreUtils,那么您可以使用此版本的uniq 函数

    sub uniq {
      my %seen;
      grep { not $seen{$_}++ } @_;
    }
    

    【讨论】: