Arrays 在Perl中为每个ID在数组中添加所有值

Arrays 在Perl中为每个ID在数组中添加所有值,arrays,perl,hash,sum,shift,Arrays,Perl,Hash,Sum,Shift,我有这张桌子: NAME | 12/31/2016 |值 AAA | 1/31/2017 | 10 AAA | 2/1/2017 | 20 AAA | 2/2/2017 | 30 AAA | 2/3/2017 | 40 AAA | 2/4/2017 | 50 名称| 2/9/2017 |价值 BBB | 2/10/2017 | 20 BBB | 2/11/2017 | 30 BBB | 2/12/2017 | 40 BBB | 2/13/2017 | 50 BBB | 2/14/2017 | 6

我有这张桌子:

NAME | 12/31/2016 |值
AAA | 1/31/2017 | 10
AAA | 2/1/2017 | 20
AAA | 2/2/2017 | 30
AAA | 2/3/2017 | 40
AAA | 2/4/2017 | 50
名称| 2/9/2017 |价值
BBB | 2/10/2017 | 20
BBB | 2/11/2017 | 30
BBB | 2/12/2017 | 40
BBB | 2/13/2017 | 50
BBB | 2/14/2017 | 60
这就是我想要的结果:

NAME | DATE | VALUE
AAA | 12/31/2016 | 150
AAA | 1/31/2017 | 140
AAA | 2/1/2017 | 120
名称|日期|值
BBB | 2/9/2017 | 200
BBB | 2/10/2017 | 180
BBB | 2/11/2017 | 150
我想做的是,对于每个有效符号,(
AAA
BBB
)我想有三行

对于每列的第一行,我希望添加所有值

例如,AAA的第1行值:

10+20+30+40+50=150
对于第2行,我只想将第二个值添加到最后一个值

例如,
AAA的第2行值

20+30+40+50=140
同样的情况也适用于
BBB

我想将日期向下移动,以便
12/31/2016
AAA
匹配,然后获得每行的前三个日期

我现在有这个代码。但这没什么用。它只是给了我一堆数字

使用严格;
使用警告;
使用Scalar::Util qw(看起来像数字);
使用数据::转储程序;
附属大学{
我看到的百分比;
grep!$seen{$}++,@;
}
我的%现金流;
我的%fields=(
ID=>0,
日期=>1,
值=>2,
);
我的@total;
我的@IDs;
我的@uniqueid;
我的日期;
我的@add;
我的$i=0;
我的价值观;
我的$counter=3;
打开(文件“try.CSV”);
while(我的$line=){
chomp($line);
my@lineVals=拆分(/\\\\/,$line);
if($lineVals[$fields{ID}]!~/^SYMBOL$/i){
推送@IDs,$lineVals[$fields{ID}];
}
@uniqueIDs=uniq(@IDs);
#将所有现金流金额推送到@cashflow
if(看起来像数字($lineVals[$fields{VALUE}])){
$lineVals[$fields{VALUE}]=~s/\r/;
push@total,$lineVals[$fields{VALUE}];
}
如果($lineVals[$fields{DATES}]=~/(\d{1,2})\/(\d{1,2})\/(\d{4})/){
$lineVals[$fields{DATES}]=sprintf('%04d%02d%02d',$3,$2,$1);
}
$cashflow{uc$lineVals[$fields{ID}]}{DATES}=$lineVals[$fields{DATES}];
$cashflow{uc$lineVals[$fields{ID}]}{VALUE}=$lineVals[$fields{VALUE}];
foreach my$ID(@uniqueIDs){
foreach my$symb(钥匙%现金流){
如果($ID=$symb){
if(看起来像数字($lineVals[$fields{VALUE}])){
$lineVals[$fields{VALUE}]=~s/\r/;
push@total,$lineVals[$fields{VALUE}];
我的$i=0;
我的$grand=0;
foreach my$val(@total){
而($i<$counter){
$grand+=$val;
打印“$grand\n”;
$i++;
}
移位@总数;
}
}
}
}
}
}
关闭文件;
我真的被这件事缠住了。我不知道该怎么处理这个问题。

一个可能的解决方案:

#!perl
use strict;
use warnings;

sub trim {
    my ($str) = @_;
    s!\A\s+!!, s!\s+\z!! for $str;
    $str
}

my $file = 'try.CSV';    
open my $fh, '<', $file or die "$0: $file: $!\n";

my ($group_name, @dates, @values);
my $sum = 0;

my $print_group = sub {
    return if !defined $group_name;
    my $format = "    %-6s|%-11s|%s\n";
    printf $format, 'NAME', 'DATE', 'VALUE';
    for my $date (@dates) {
        printf $format, $group_name, $date, $sum;
        $sum -= shift @values if @values;
    }
};

while (my $line = readline $fh) {
    my ($name, $date, $value) = map trim($_), split /\|/, $line;
    if ($name eq 'NAME') {
        $print_group->();
        $group_name = undef;
        @dates = $date;
        @values = ();
        $sum = 0;
        next;
    }
    $group_name ||= $name;
    push @dates, $date if @dates < 3;
    push @values, $value if @values < 2;
    $sum += $value;
}
$print_group->();
用于从字符串中删除前导/尾随空格的辅助函数。我们正在使用
作为此处的
s
分隔符,因为
/
打破了SO的语法突出显示。耸耸肩

my $file = 'try.CSV';    
open my $fh, '<', $file or die "$0: $file: $!\n";
我们设置了一些要在循环迭代中保留的状态变量
$group\u name
是我们当前正在处理的组的名称,
@dates
是我们到目前为止看到的保存日期,
@values
是我们到目前为止看到的保存值
$sum
是当前组中所有值的运行总和,它从
0
开始

my $print_group = sub {
    return if !defined $group_name;
    my $format = "    %-6s|%-11s|%s\n";
    printf $format, 'NAME', 'DATE', 'VALUE';
    for my $date (@dates) {
        printf $format, $group_name, $date, $sum;
        $sum -= shift @values if @values;
    }
};
用于打印单个组的输出的辅助函数。如果未设置
$group\u name
,则我们尚未处理当前组的任何输入,因此不执行任何操作并返回。否则,我们将打印一个
NAME | DATE |值
标题,后面是
@dates
中每个元素的一行数据。对于每个
$date
,我们输出当前组名(例如
AAA
)、
$date
,以及值的总和(所有值都使用
printf
进行了良好的格式化)。最初,
$sum
是所有组值的总和,但在第一次迭代后,我们开始从
@值中减去值:如果输入中的值列表是
x1
x2
x3
x4
,…,则
$sum
最初是
x1+x2+x3+x4+…
,这就是第一行输出的内容。然后我们减去
x1
,因此下一行得到
x1+x2+x3+x4+…-x1
,即x2+x3+x4+…
。然后我们减去x2,第三行数据得到x3+x4+…

while (my $line = readline $fh) {
    my ($name, $date, $value) = map trim($_), split /\|/, $line;
我们的主回路。我们读取一行输入,在
|
上拆分它,然后修剪每个字段

    if ($name eq 'NAME') {
        $print_group->();
        $group_name = undef;
        @dates = $date;
        @values = ();
        $sum = 0;
        next;
    }
如果
$name
'name'
,则这是新组的开始。如果有,打印当前组的输出(
$Print\u group->()
如果没有当前组,则不执行任何操作),然后将状态变量重置回初始值,除了
@dates
,该值由标题行中的
$date
值填充。然后开始循环的下一次迭代,因为我们已经完成了这一行

    $group_name ||= $name;
    push @dates, $date if @dates < 3;
    push @values, $value if @values < 2;
    $sum += $value;
在循环结束时,我们刚刚处理完一个组,因此我们也需要在此处调用
$print\u group

一个可能的解决方案:

#!perl
use strict;
use warnings;

sub trim {
    my ($str) = @_;
    s!\A\s+!!, s!\s+\z!! for $str;
    $str
}

my $file = 'try.CSV';    
open my $fh, '<', $file or die "$0: $file: $!\n";

my ($group_name, @dates, @values);
my $sum = 0;

my $print_group = sub {
    return if !defined $group_name;
    my $format = "    %-6s|%-11s|%s\n";
    printf $format, 'NAME', 'DATE', 'VALUE';
    for my $date (@dates) {
        printf $format, $group_name, $date, $sum;
        $sum -= shift @values if @values;
    }
};

while (my $line = readline $fh) {
    my ($name, $date, $value) = map trim($_), split /\|/, $line;
    if ($name eq 'NAME') {
        $print_group->();
        $group_name = undef;
        @dates = $date;
        @values = ();
        $sum = 0;
        next;
    }
    $group_name ||= $name;
    push @dates, $date if @dates < 3;
    push @values, $value if @values < 2;
    $sum += $value;
}
$print_group->();
用于从字符串中删除前导/尾随空格的辅助函数。我们正在使用
作为此处的
s
分隔符,因为
/
打破了SO的语法突出显示。耸耸肩

my $file = 'try.CSV';    
open my $fh, '<', $file or die "$0: $file: $!\n";
我们设置了一些要在循环迭代中保留的状态变量。
}
$print_group->();