英文:
Add the sum of array values to hash (map / grep)
问题
my %sum_payments = map {
my $payment_id = $_->{payment_id};
my $total_amount = 0;
$total_amount += $_->{payment_amount} for grep { $_->{payment_id} == $payment_id } @$payments;
$payment_id => $total_amount;
} grep { $_->{receipt_id} eq '100' } @$payments;
英文:
I've been trying to figure out this one-liner, and can't seem to get it to work.
Say I have this reference to an array of hashrefs:
my $payments = [
{
'receipt_id' => '100',
'payment_id' => '1',
'payment_amount' => 20,
},
{
'receipt_id' => '100',
'payment_id' => '1',
'payment_amount' => 30,
},
{
'receipt_id' => '100',
'payment_id' => '2',
'payment_amount' => 40,
},
{
'receipt_id' => '200',
'payment_id' => '1',
'payment_amount' => 20,
},
];
What I'd like to do is create a hash where the key is the payment_id, and the value is the sum of all payment_amounts for a particular receipt_id.
I can do:
my %sum_payments = map { $_->{payment_id} => $_->{payment_amount} }
grep { $_->{receipt_id} eq '100' }
@$payments;
And get:
1 => 30,
2 => 40,
But when I try:
my %sum_payments = map { $_->{payment_id} => (sum map { $_->{payment_amount} }) }
grep { $_->{receipt_id} eq '100' }
@$payments;
It throws errors.
How can I write a one-liner using map / grep to get:
1 => 50,
2 => 40,
Also, I know there are many other ways to achieve the same thing. I'm interested to know if this is possible using a single statement with map / grep.
答案1
得分: 1
The problem with the approach you are trying is that to calculate the sum with a function like that requires you to somehow build up a list for each payment_id
before you pass it to the function. It may be possible to do this all in one line, but I think it is much more straightforward to use a for
loop in this case:
my %sum_payments;
for (@{ $payments }) {
if ($_->{receipt_id} eq '100') {
$sum_payments{ $_->{payment_id} } += $_->{payment_amount};
}
}
Output:
'1' => 50,
'2' => 40
英文:
The problem with the approach you are trying is that to calculate the sum with a function like that requires you to somehow build up a list for each payment_id
before you pass it to the function. It may be possible to do this all in one line, but I think it is much more straightforward to use a for
loop in this case:
my %sum_payments;
for (@{ $payments }) {
if ($_->{receipt_id} eq '100') {
$sum_payments{ $_->{payment_id} } += $_->{payment_amount};
}
}
Output:
'1' => 50,
'2' => 40
答案2
得分: 0
你只需要将一些内容累加到目标哈希表中,例如直接分配到目标哈希表中:
my %sum_payments;
map { $sum_payments{$_->{payment_id}} += $_->{payment_amount} }
grep { $_->{receipt_id} eq '100' }
@{$payments};
这类似于@toolic的答案,但如他所说,可读性较差(而且我认为在大输入数据上效率较低,因为grep会建立一个中间列表),而且有些人不喜欢这样使用map
。
英文:
You just need something to accumulate into - such as assigning directly into the destination hash:
my %sum_payments;
map { $sum_payments{$_->{payment_id}} += $_->{payment_amount} }
grep { $_->{receipt_id} eq '100' }
@$payments;
This is similar to @toolic's answer but, as that says, less readable (and I think less inefficient on large input due to grep building an intermediate list), and some people don't like map
used like this.
答案3
得分: 0
我不会回答问题,只提供翻译:
"我对确切的要求有点不确定,但我认为完整的结果应该是
payment_id receipt_id sum of amounts
1 => 100 => 50 (20+30)
200 => 20
2 => 100 => 40
然后
my %recd;
for my $hr (@$payments) {
$recd{$hr->{payment_id}}{$hr->{receipt_id}} += $hr->{payment_amount}
}
dd \%recd; # 使用 Data::Dump; 或使用核心的 Data::Dumper
打印出
{ 1 => { 100 => 50, 200 => 20 }, 2 => { 100 => 40 } }
对于每个ID,都有所有收据的键,每个键都有一个总金额值。
我仍然不确定为什么问题中(以及所有答案中)要突出显示“receipt_id”为100,因为想法是每次运行程序只需要一个?如果是这样,那么这个答案就有些过度了;可以使用其他一些方法。
另一方面,也有可能会有进一步的需求,为什么不使用所有数据来构建呢?然后可以根据需要直接查找特定的数据。
问题中的具体map
+grep
尝试不会奏效,因为map
在每次迭代中只有一条记录(散列引用),所以它不能简单地对所有记录的payment_amount
求和。
所以我认为,除非通过某种形式的不好的技巧最终将其正式转化为单个语句并分配给散列,否则无法合理地实现这一点。"
英文:
I'm a bit unsure of the exact requirements but I take it that the complete result would be
payment_id receipt_id sum of amounts
1 => 100 => 50 (20+30)
200 => 20
2 => 100 => 40
Then
my %recd;
for my $hr (@$payments) {
$recd{$hr->{payment_id}}{$hr->{receipt_id}} += $hr->{payment_amount}
}
dd \%recd; # w/ use Data::Dump; Or use core Data::Dumper
Prints
{ 1 => { 100 => 50, 200 => 20 }, 2 => { 100 => 40 } }
For each id there are keys of all receipts, each having for a value the total amounts.
Still not sure why receipt_id
of 100
is singled out in the question (and all answers) ... because the idea is that only one is needed in each run of the program? If that is so then this answer is an overkill; use some of the others.
On the other hand, it's plausible that there'll be further needs and why not build it with all data. Then the specific ones can be plucked out as needed, by a direct lookup.
The specific map
+grep
attempt from the question won't work because map
in each iteration has one record (hashref) on hand so it can't simply sum up payment_amount
across all of them.
So I think that no, one can't reasonably have that (other than by some bad acrobatics formally cast in a single statement eventually assigning to a hash, that is).
答案4
得分: -1
You can use [List::Util
模块中的 reduce
来在单次遍历数组引用时计算值,使用 fold:
#!/usr/bin/env perl
use strict;
use warnings;
use List::Util qw/reduce/;
use Data::Dumper;
my $payments =
[
{
'receipt_id' => '100',
'payment_id' => '1',
'payment_amount' => 20,
},
{
'receipt_id' => '100',
'payment_id' => '1',
'payment_amount' => 30,
},
{
'receipt_id' => '100',
'payment_id' => '2',
'payment_amount' => 40,
},
{
'receipt_id' => '200',
'payment_id' => '1',
'payment_amount' => 20,
},
];
# 结果是一个哈希引用
my $sum_payments =
reduce {
if ($b->{'receipt_id'} eq '100') {
$a->{$b->{'payment_id'}} += $b->{'payment_amount'};
}
$a
} {}, @$payments;
print Dumper($sum_payments);
输出:
$VAR1 = {
'1' => 50,
'2' => 40
};
<details>
<summary>英文:</summary>
You can use [`reduce` from the standard `List::Util` module][1] to compute the values in a single pass over your arrayref using a [fold][2]:
```perl
#!/usr/bin/env perl
use strict;
use warnings;
use List::Util qw/reduce/;
use Data::Dumper;
my $payments =
[
{
'receipt_id' => '100',
'payment_id' => '1',
'payment_amount' => 20,
},
{
'receipt_id' => '100',
'payment_id' => '1',
'payment_amount' => 30,
},
{
'receipt_id' => '100',
'payment_id' => '2',
'payment_amount' => 40,
},
{
'receipt_id' => '200',
'payment_id' => '1',
'payment_amount' => 20,
},
];
# Results in a hashref
my $sum_payments =
reduce {
if ($b->{receipt_id} eq '100') {
$a->{$b->{payment_id}} += $b->{payment_amount};
}
$a
} {}, @$payments;
print Dumper($sum_payments);
prints out
$VAR1 = {
'1' => 50,
'2' => 40
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论