You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

优化建议

  1. 避免重复遍历:通过预构建索引(%fees_index),把每次grep的O(n)操作变成O(1)的哈希查找,大幅提升效率,尤其适合数据量大的场景。
  2. 去重处理主bill:通过%bill_link_map确保每个主bill_id只处理一次,从根源避免重复添加数据。
  3. 可选:用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 08:15:29