保留第一行后删除包含行的重复值

时间:2016-12-17 16:37:59

标签: perl

我有一个标签分隔的大文件,如下所示:

input.txt中

a   b   c
s   t   e
a   b   c
f   q   y
r   e   x

删除此文件中的重复行(行),我使用:

my %seen;
my @lines;

while (<>) {
    my @cols = split /\s+/;
    unless ($seen{$cols[0]}++) {
        push @lines, $_;
    }
}

print @lines;

这里的输出是:

a   b   c
s   t   e
f   q   y
r   e   x

现在,如果我想要删除包含重复值的那些行(意味着:该值一旦出现在上行/列中的任何位置,此处为“e”)并且仅保留包含行的最高值,请建议将是什么最常用的方法是记住我的输入文件非常大,有很多列和行。

我想要的上述input.txt的模型输出将是:

a   b   c
s   t   e
f   q   y

谢谢

2 个答案:

答案 0 :(得分:4)

您还需要遍历@cols并检查每个项目,而不仅仅是第一个项目$cols[0]。 你需要像

这样的东西
unless ($seen{$cols[0]}++ || $seen{$cols[1]}++ || $seen{$cols[2]}++ ...) {
    push @lines, $_;
}

当然,如果您事先不知道列数,这将是糟糕的风格,不可能。

我会用grep

来做
my %seen;
my @lines;

while (<DATA>) {
    my @cols = split /\s+/;
    unless ( grep { $seen{$_}++ } @cols ) {
        push @lines, $_;
    }
}

print @lines;


__DATA__
a   b   c
s   t   e
a   b   c
f   q   y
r   e   x

输出:

a   b   c
s   t   e
f   q   y

grep处理列表{ $seen{$_}++ }中每个元素的curlies @cols之间的代码,并返回(在标量上下文中)评估为true的项目数。

这不是最快的方法,因为它总是迭代整个数组(即使第一次评估对于您的特定测试也是如此)。但试一试;或许这对你来说足够快。

答案 1 :(得分:2)

正如我在评论中写的那样,split /\s+/很少是正确的

您使用重复字段错误处理行的解决方案

使用核心grep模块中的any替换List::Util也更有效率

我建议您将每行的字段存储在哈希%cols中,就像这样

use strict;
use warnings 'all';

use List::Util 'any';

my ( @lines, %seen );

while ( <DATA> ) {

    my %cols = map { $_ => 1 } split;

    push @lines, $_ unless any { $seen{$_}++ } keys %cols;
}

print for @lines;

__DATA__
a   b   c
p   p   p
p   q   r
s   t   e
a   b   c
f   q   y
r   e   x

输出

a   b   c
p   p   p
s   t   e

即使这可能不是您想要的,因为省略了f q y行,因为q已经被&#34;看到&#34;在省略的行p q r中。在这种情况下,您必须澄清所需的行为