将数组值的总和添加到哈希表(map / grep)中。

huangapple go评论68阅读模式
英文:

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 =
  [
   {
    &#39;receipt_id&#39;     =&gt; &#39;100&#39;,
    &#39;payment_id&#39;     =&gt; &#39;1&#39;,
    &#39;payment_amount&#39; =&gt; 20,
   },
   {
    &#39;receipt_id&#39;     =&gt; &#39;100&#39;,
    &#39;payment_id&#39;     =&gt; &#39;1&#39;,
    &#39;payment_amount&#39; =&gt; 30,
   },
   {
    &#39;receipt_id&#39;     =&gt; &#39;100&#39;,
    &#39;payment_id&#39;     =&gt; &#39;2&#39;,
    &#39;payment_amount&#39; =&gt; 40,
   },
   {
    &#39;receipt_id&#39;     =&gt; &#39;200&#39;,
    &#39;payment_id&#39;     =&gt; &#39;1&#39;,
    &#39;payment_amount&#39; =&gt; 20,
   },
  ];

# Results in a hashref
my $sum_payments =
  reduce {
    if ($b-&gt;{receipt_id} eq &#39;100&#39;) {
      $a-&gt;{$b-&gt;{payment_id}} += $b-&gt;{payment_amount};
    }
    $a
  } {}, @$payments;

print Dumper($sum_payments);

prints out

$VAR1 = {
          &#39;1&#39; =&gt; 50,
          &#39;2&#39; =&gt; 40
        };

huangapple
  • 本文由 发表于 2023年5月11日 06:19:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76222925.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定