原始的perl数组已经排序,如下所示:
Original ARRARY:
ccc-->2
ccc-->5
abc-->3
abc-->7
cb-->6
我希望得到以下结果:
FINAL ARRARY:
ccc-->7
abc-->10
cb-->6
问题:
你可以为此创建一个子程序吗?
这是原创。我使用的子程序:
sub read_final_dev_file {
$dfcnt=0;
$DEVICE_ANZSUMZW=0;
$DEVICE_ANZSUM=0;
open(DATA,"$log_dir1/ALLDEVSORT.$log_file_ext1") || die ("Cannot Open Logfile: $log_dir1/$log_DEV_name.$log_file_ext1 !!!!");
@lines = <DATA>;
close(DATA);
chomp(@lines); # erase the last sign from a string
foreach $logline (@lines) {
if ($logline =~ /(.*)-->(.*)/) {
$DEVICE_CODE[$dfcnt] = $1;
$DEVICE_ANZAHL[$dfcnt] = $2;
print "DEVICE_final = $DEVICE_CODE[$dfcnt], D_ANZAHL_final = $DEVICE_ANZAHL[$dfcnt]\n";
if ($dfcnt > 0 ) {
if ( $DEVICE_CODE[$dfcnt] eq $DEVICE_CODE[$dfcnt-1] ) {
$DEVICE_ANZSUM = $DEVICE_ANZAHL[$dfcnt] + $DEVICE_ANZAHL[$dfcnt-1];
$DEVICE_ANZSUMZW = $DEVICE_ANZSUM++;
#$DEVICE_ANZSUM = $DEVICE_ANZAHL[$dfcnt]++;
#print "DEVICE_ANZAHL = $DEVICE_ANZAHL[$dfcnt],DEVICE_ANZAHL -1 = $DEVICE_ANZAHL[$dfcnt-1]\n";
print "DEVICE_eq = $DEVICE_CODE[$dfcnt], D_ANZAHL_eq = $DEVICE_ANZAHL[$dfcnt],DEVANZSUM = $DEVICE_ANZSUM,COUNT = $dfcnt\n";
}#end if
if ( $DEVICE_CODE[$dfcnt] ne $DEVICE_CODE[$dfcnt-1] ) {
#$DEVICE_ANZSUM=0;
#splice(@data3,$dfcnt+2,1) if ($DEVICE_ANZSUM > 1);
push (@data3,$DEVICE_ANZSUMZW) if ($DEVICE_ANZSUM > 1);
push (@data3,$DEVICE_ANZAHL[$dfcnt]) if ($DEVICE_ANZSUM == 0);
if ( $DEVICE_CODE[$dfcnt] ne $DEVICE_CODE[$dfcnt-1] ) {
$DEVICE_ANZSUM=0;
}
print "DEVICE_ne = $DEVICE_CODE[$dfcnt], D_ANZAHL_ne = $DEVICE_ANZAHL[$dfcnt], DEVANZSUM = $DEVICE_ANZSUM\n";
}#end if
}#end if $dfcnt
$dfcnt++;
}#end if logline
}#end for
print "@labels3\n";
print "@data3\n";
}#end sub read_final_dev_file
答案 0 :(得分:2)
可能不是最好的方式,但这是看到LeoNerd回答之后想到的,因为我没有生产中的CPAN访问权限,并且从来没有模块存在:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my @input = (
[ ccc => 2 ],
[ ccc => 5 ],
[ abc => 3 ],
[ abc => 7 ],
[ cb => 6 ],
);
my %output;
$output{$_->[0]} += $_->[1] for @input;
print Dumper \%output;
my @output = map { [ $_ => $output{$_} ] } keys(%output);
print Dumper \@output;
输出:
$VAR1 = {
'abc' => 10,
'cb' => 6,
'ccc' => 7
};
$VAR1 = [
['abc', 10],
['cb', 6],
['ccc', 7],
];
答案 1 :(得分:0)
您可以使用List::UtilsBy::partition_by
通过第一个字符串将原始列表分组到分区中:
use List::UtilsBy qw( partition_by );
my @input = (
[ ccc => 2 ],
[ ccc => 5 ],
[ abc => 3 ],
[ abc => 7 ],
[ cb => 6 ],
);
my %sets = partition_by { $_->[0] } @input;
现在你有一个哈希,由前导字符串键入,其值是所有ARRAY
引用首先使用该键。您现在可以通过映射包含数字的$_->[1]
来对它们中的值求和:
use List::Util qw( sum );
my %totals;
foreach my $key ( keys %sets ) {
$totals{$key} = sum map { $_->[1] } @{ $sets{$key} };
}
如果您倾向于使用更紧凑和功能性更强的代码,那么您可以在此处使用新的pairmap
;使整个事物在一行中表达出来:
use List::UtilsBy qw( partition_by );
use List::Util qw( pairmap sum );
my %totals = pairmap { $a => sum map { $_->[1] } @$b }
partition_by { $_->[0] } @input;
编辑:我应该补充一点,即使您在原始问题中声明数组已排序,但此解决方案并不需要对其进行排序。它会愉快地以任何顺序接受输入。
答案 2 :(得分:0)
通过使用哈希来跟踪计数而不是数组,可以大大简化子例程。以下使用数组@devices
来跟踪订单,使用哈希%device_counts
来跟踪计数:
my @devices;
my %device_counts;
while (<DATA>) { # Read one line at a time from DATA
if (/(.*)-->(.*)/) { # This won't extract newlines so no need to chomp
if (!exists $device_counts{$1}) {
push @devices, $1; # Add to the array the first time we encounter a device
}
$device_counts{$1} += $2; # Add to the count for this device
}
}
for my $device (@devices) {
printf "%s-->%s\n", $device, $device_counts{$device};
}