我正在编写一个解析“pure-ftpwho -s”命令的脚本,以获取当前传输的列表。但是当用户断开与FTP的连接并重新连接并恢复传输时,该文件会显示两次。我想用Perl删除鬼影。解析之后,这是arrayref的样子(使用Data :: Dumper转储)
$VAR1 = [
{
'status' => 'DL',
'percent' => '20',
'speed' => '10',
'file' => 'somefile.txt',
'user' => 'user1',
'size' => '14648'
},
{
'status' => 'DL',
'percent' => '63',
'speed' => '11',
'file' => 'somefile.txt',
'user' => 'user1',
'size' => '14648'
},
{
'status' => 'DL',
'percent' => '16',
'speed' => '60',
'file' => 'somefile.txt',
'user' => 'user2',
'size' => '14648'
}
];
这里user1和user2正在下载相同的文件,但是user1出现两次,因为第一个是“ghost”。检查和删除不需要的元素(在这种情况下是arrayref的第一个元素)的最佳方法是什么。要检查的条件是 - 如果“file”键和“user”键相同,则删除包含较小值“percent”键的hashref(如果它们相同则删除除1之外的所有键)。
答案 0 :(得分:4)
如果原始arrayref中的顺序无关紧要,则应该有效:
my %users;
my @result;
for my $data (@$arrayref) {
push @{ $users{$data->{user}.$data->{file}} }, $data;
}
for my $value (values %users) {
my @data = sort { $a->{percent} <=> $b->{percent} } @$value;
push @result, $data[-1];
}
这绝对可以提高效率。
答案 1 :(得分:4)
在这种情况下,正确的解决方案是在解析日志文件时使用哈希。将所有信息放入哈希值,例如%log
,由user
和file
键入:
$log{$user}->{$file} = {
'status' => 'DL',
'percent' => '20',
'speed' => '10',
'size' => '14648'
};
等。日志文件中的后期条目将覆盖之前的条目。或者,您可以选择覆盖完成率较低的条目和完成率较高的条目。
使用散列可以解决许多完全多余的代码,这些代码可以解决错误数据结构的选择问题。
答案 2 :(得分:3)
对于它的价值,这是我的(略微)替代方法。同样,它不保留原始顺序:
my %most_progress;
for my $data ( sort { $b->{percent} <=> $a->{percent} } @$data ) {
next if exists $most_progress{$data->{user}.$data->{file}};
$most_progress{$data->{user}.$data->{file}} = $data;
}
my @clean_data = values %most_progress;
答案 3 :(得分:2)
这将保留顺序:
use strict;
use warnings;
my $data = [ ... ]; # As posted.
my %pct;
for my $i ( 0 .. $#{$data} ){
my $r = $data->[$i];
my $k = join '|', $r->{file}, $r->{user};
next if exists $pct{$k} and $pct{$k}[1] >= $r->{percent};
$pct{$k} = [$i, $r->{percent}];
}
@$data = @$data[sort map $_->[0], values %pct];
答案 4 :(得分:0)
my %check;
for (my $i = 0; $i <= $#{$arrayref}; $i++) {
my $transfer = $arrayref->[$i];
# check the transfer for user and file
my $key = $transfer->{user} . $transfer->{file};
$check{$key} = { } if ( !exists $check{$key} );
if ( $transfer->{percent} <= $check{$key}->{percent} ) {
# undefine this less advanced transfer
$arrayref->[$i] = undef;
} else {
# remove the other transfer
$arrayref->[$check{$key}->{index}] = undef if exists $check{$key}->{index};
# set the new standard
$check{$key} = { index => $i, percent => $transfer->{percent} }
}
}
# remove all undefined transfers
$arrayref = [ grep { defined $_ } @$arrayref ];
答案 5 :(得分:0)
use Perl6::Gather;
my @cleaned = gather {
my %seen;
for (sort { $b->{percent} <=> $a->{percent} } @$data) {
take unless $seen{ $_->{user} . $_->{file} }++;
}
};