Perl中如何将两个哈希数组转换为指定数据结构并优化?
解决Perl中哈希数组合并为指定哈希引用的问题
需求说明
需要将两个哈希数组($mappings和$fees)转换为一个哈希引用:
- 键为
$mappings中的bill_id - 值为该
bill_id及其所有linked_bill_id对应的fees条目数组
原始数据结构
$mappings 结构
$mappings = [ { bill_id => '100', linked_bill_id => '1000', }, { bill_id => '100', linked_bill_id => '1001', }, { bill_id => '200', linked_bill_id => '2000', }, { bill_id => '200', linked_bill_id => '2001', }, { bill_id => '200', linked_bill_id => '2002', }, ];
$fees 结构
$fees = [ { bill_id => '100', payment_id => '500', version => 1, has_fee => 0, }, { bill_id => '100', payment_id => '501', version => 2, has_fee => 1, }, { bill_id => '1000', payment_id => '502', version => 1, has_fee => 0, }, { bill_id => '1001', payment_id => '503', version => 1, has_fee => 0, }, { bill_id => '200', payment_id => '504', version => 1, has_fee => 0, }, { bill_id => '2000', payment_id => '505', version => 1, has_fee => 0, }, { bill_id => '2001', payment_id => '506', version => 1, has_fee => 0, }, { bill_id => '2002', payment_id => '507', version => 1, has_fee => 1, }, ];
预期结果
$VAR1 = { '100' => [ { 'bill_id' => '100', 'payment_id' => '500', 'version' => 1, 'has_fee' => 0, }, { 'bill_id' => '100', 'payment_id' => '501', 'version' => 2, 'has_fee' => 1, }, { 'bill_id' => '1000', 'payment_id' => '502', 'version' => 1, 'has_fee' => 0, }, { 'bill_id' => '1001', 'payment_id' => '503', 'version' => 1, 'has_fee' => 0, }, ], '200' => [ { 'bill_id' => '200', 'payment_id' => '504', 'version' => 1, 'has_fee' => 0, }, { 'bill_id' => '2000', 'payment_id' => '505', 'version' => 1, 'has_fee' => 0, }, { 'bill_id' => '2001', 'payment_id' => '506', 'version' => 1, 'has_fee' => 0, }, { 'bill_id' => '2002', 'payment_id' => '507', 'version' => 1, 'has_fee' => 1, }, ] };
现有代码问题分析
你编写的代码会出现重复数据,核心原因是:$mappings中同一个bill_id存在多条记录(比如200有3条),循环遍历@$mappings时,会对同一个bill_id重复执行push操作,导致相同的fee条目被多次添加到数组中。
此外,每次通过grep查找fee条目都会遍历整个$fees数组,数据量大时效率极低。
正确实现方案
步骤1:构建主bill与关联bill的映射(去重)
先遍历$mappings,为每个主bill_id整理出所有关联的bill_id(包括自身),用哈希集合避免重复:
my %bill_link_map; foreach my $entry (@$mappings) { my $main_bill = $entry->{bill_id}; # 将主bill自身加入集合 $bill_link_map{$main_bill}{$main_bill} = 1; # 添加关联的bill_id $bill_link_map{$main_bill}{$entry->{linked_bill_id}} = 1; }
步骤2:构建fees的索引(按bill_id分组)
将$fees按bill_id分组,后续可以直接通过bill_id快速获取对应的fee条目:
my %fees_index; foreach my $fee (@$fees) { push @{$fees_index{$fee->{bill_id}}}, $fee; }
步骤3:生成最终的哈希引用
遍历主bill映射,将对应主bill和关联bill的fee条目合并:
my $bill_fees = {}; foreach my $main_bill (keys %bill_link_map) { my @all_fees; foreach my $bill_id (keys %{$bill_link_map{$main_bill}}) { # 如果该bill_id有对应的fee条目,就加入数组 push @all_fees, @{$fees_index{$bill_id}} if exists $fees_index{$bill_id}; } $bill_fees->{$main_bill} = \@all_fees; }
合并后的完整代码
my %bill_link_map; foreach my $entry (@$mappings) { my $main_bill = $entry->{bill_id}; $bill_link_map{$main_bill}{$main_bill} = 1; $bill_link_map{$main_bill}{$entry->{linked_bill_id}} = 1; } my %fees_index; foreach my $fee (@$fees) { push @{$fees_index{$fee->{bill_id}}}, $fee; } my $bill_fees = {}; foreach my $main_bill (keys %bill_link_map) { my @all_fees; foreach my $bill_id (keys %{$bill_link_map{$main_bill}}) { push @all_fees, @{$fees_index{$bill_id}} if exists $fees_index{$bill_id}; } $bill_fees->{$main_bill} = \@all_fees; }
优化建议
- 避免重复遍历:通过预构建索引(
%fees_index),把每次grep的O(n)操作变成O(1)的哈希查找,大幅提升效率,尤其适合数据量大的场景。 - 去重处理主bill:通过
%bill_link_map确保每个主bill_id只处理一次,从根源避免重复添加数据。 - 可选:用map简化代码:如果偏好链式操作,也可以用
map和grep实现,但可读性和效率不如预构建索引的方式,示例如下(仅作参考):
# 先提取唯一的主bill_id列表 my %unique_main_bills = map { $_->{bill_id} => 1 } @$mappings; my @main_bills = keys %unique_main_bills; my $bill_fees = { map { my $main_bill = $_; # 获取当前主bill的所有关联bill_id(包括自己) my %linked_bills = map { $_->{linked_bill_id} => 1 } grep { $_->{bill_id} eq $main_bill } @$mappings; $linked_bills{$main_bill} = 1; # 筛选并合并对应的fee条目 $main_bill => [ grep { exists $linked_bills{$_->{bill_id}} } @$fees ] } @main_bills };
注:这个版本虽然简洁,但每次grep都会遍历数组,数据量大时效率不如预构建索引的方案。
内容的提问来源于stack exchange,提问作者ComputersAreNeat
相关产品推荐
相关产品推荐

