使用公共字段将许多行合并为一行

时间:2014-05-15 15:40:24

标签: perl array-merge

我有一个如下文件。

1111|p1
1111|p2
1111|p3
1111|p4
1111|p5
1111|p6
2222|p1
2222|p2

依旧......

一个人(1111)每行可以有一个产品。

我需要输出如下

1111|row1|p1|p2|p3|p4|p5
1111|row2|p6
2222|p1|p2 
3333|p1|p2|p3 

所以对于一行只允许到p5,剩下的应该在第2行。 请帮我在Perl中以这种方式获得输出。

2 个答案:

答案 0 :(得分:4)

使用数组散列来存储每个人的产品:

#!/usr/bin/perl
use warnings;
use strict;

my %owns;
while (<DATA>) {
    chomp;
    my ($person, $product) = split /\|/;
    push @{ $owns{$person} }, $product;
}

for my $person (keys %owns) {
    my @products = @{ $owns{$person} };
    if (@products > 5) {
        my $row = 1;
        while (@products) {
            my @five = splice @products, 0, 5;
            print join '|', $person, "row$row", @five;
            print "\n";
            $row++;
        }
    } else {
        print join '|', $person, @products;
        print "\n";
    }
}


__DATA__
1111|p1
1111|p2
1111|p3
1111|p4
1111|p5
1111|p6
2222|p1
2222|p2

答案 1 :(得分:0)

没有什么比试图聪明的程序员更糟糕了。

但是,以下使用正则表达式来执行相同的过滤器:

use strict;
use warnings;

my $data = do {local $/; <DATA>};

$data =~ s{^((\d+)\|.*\n(?:\2\|.*\n)+)}{
    my ($whole, $header) = ($1, $2);
    my @nums = map {/\|(.*)/} split "\n", $whole;
    my $return = '';
    if (@nums > 5) {
        for (my $i = 1; @nums; $i++) {
            $return .= join('|', $header, "row$i", splice @nums, 0, 5) . "\n";
        }
    } else {
        $return = join('|', $header, @nums) . "\n";
    }
    $return;
}emg;

print $data;

__DATA__
1111|p1
1111|p2
1111|p3
1111|p4
1111|p5
1111|p6
2222|p1
2222|p2

输出:

1111|row1|p1|p2|p3|p4|p5
1111|row2|p6
2222|p1|p2