我有一张这样的表:
+ chr13 25017807 6
+ chr10 128074490 1
- chr7 140968671 1
+ chr10 79171976 3
- chr7 140968671 1
+ chr12 4054997 6
+ chr13 25017807 6
+ chr15 99504255 6
- chr8 91568709 5
它已经作为字符串变量(外部shell脚本的返回值)读入Perl。我需要删除重复的行并按最后一列对表进行排序,然后将其打印出来。我应该如何在Perl中做到这一点?谢谢!
答案 0 :(得分:2)
假设数据包含在字符串$string
中,此解决方案将起作用:
my %seen; # just needed to remove duplicates
my $deduped_string =
join "\n", # 6. join the lines to a single string
map { join(" ", @$_) } # 5. join the fields of each line to a string
sort { $a->[-1] <=> $b->[-1] } # 4. sort arrayrefs by last field, numerically
map { [split] } # 3. split line into fields, store in anon arrayref
grep { not $seen{$_}++ } # 2. dedupe the lines
split /\n/, $string; # 1. split string into lines
这个巨大的表达从底部向顶部(或从右到左)执行。它由多个可组合的变压器和滤波器组成:
map {BLOCK} LIST
将块中的代码应用于列表的每个值。它以元素方式转换列表。grep {BLOCK} LIST
从块返回true的列表中选择那些元素。因此,它过滤列表并仅输出满足特定条件的元素。sort {BLOCK} LIST
对列表进行调整。如果$a
小于$b
,则块必须返回-1;如果大于<=>
,则返回1,如果相等,则返回0。 join STRING, LIST
运算符以这种方式以数字方式比较标量。如果省略sort函数,则使用字符串比较。split REGEX, STRING
将列表中的元素与其间的字符串连接起来。split
将字符串分成几部分。正则表达式与分隔符匹配(通常不返回)。 join
和$_
可视为反向操作。如果省略该字符串,则使用split /\s+/, $_
。省略正则表达式时,它与my @sorted_data =
map { $_->[0] } # 3. map back to the orginal value
sort { $a->[1] <=> $b->[1] } # 2. sort by the special key
map { [$_, create_the_key($_)] } # 1. annotate each value with a key
@data;
的工作方式类似,即在每个空格字符处拆分。该解决方案的核心是 Schwartzian变换,这是一种技术/习惯用法,可通过昂贵的计算密钥进行廉价排序。在它的一般形式中,它是
{{1}}
在我的具体情况下,特殊键是每条记录的最后一列;为了从注释数据中获取原始数据(或等效形式),我将这些字段连接在一起。正如 mpapec 指出的那样,我也可以通过变换携带原始线;这样可以保留线条的原始对齐方式。
答案 1 :(得分:1)
过滤掉重复的行,并按最后一列的排序
排序perl -ane 'next if $s{$_}++; push @r,[$_,@F]}{ print $$_[0] for sort { $$a[-1] <=> $$b[-1] } @r' file
几乎相同,
use strict;
use warnings;
open my $fh, "file" or die $!;
my (%seen_line, @result_unique_lines);
while (<$fh>) {
# $_ => content of current line
# skip current if it's duplicate
next if $seen_line{$_}++;
my @line_values = split;
push @result_unique_lines, [$_, @line_values];
}
close $fh;
# sort lines
@result_unique_lines = sort { $a->[-1] <=> $b->[-1] } @result_unique_lines;
for my $aref (@result_unique_lines) {
my $line = $aref->[0];
print $line;
}
答案 2 :(得分:1)
对于初学者,我这样做:
use strict; use warnings;
my $file = "table.txt";
open(my $fh, "<", $file) || die "Can't open $file: $!\n";
my @lines;
# read the file and save a transformed version to @lines
while (my $line = <$fh>) {
chomp($line); # remove final newline
$line =~ s/ +/:/gi; # make ":" the new separator
my @fields = split(/:/,$line); # split at the separator
my $newline = "$fields[4]:$fields[1]:$fields[2]:$fields[3]"; # reorder fields
push(@lines, $newline); # save the new line
}
@lines = sort(@lines); # sort lines alphabetically:
# duplicate lines are now consecutive
my $uniqline=""; # the last unique line
foreach my $line (@lines) {
# do this if the current line isn't string-equal to the last line
# (i.e. skip all lines that are equal to the previous line)
if ($uniqline ne $line) {
$uniqline = $line; # remember the last line
# print fields in original order
my @fields = split(/:/,$line);
printf(" %s %7s %11s %s\n",$fields[1],$fields[2],$fields[3],$fields[0]);
}
}
我的结果略有不同......
+ chr10 128074490 1
- chr7 140968671 1
+ chr10 79171976 3
- chr8 91568709 5
+ chr12 4054997 6
+ chr13 25017807 6
+ chr15 99504255 6