CSV文件中的重复列

时间:2014-01-21 17:47:37

标签: perl csv hash

我有一个CSV文件,我希望在Perl中变成一个hashrefs数组。这是一个已解决的问题。复杂的是这个文件包含重复的列名,我想映射重复的列名,例如,如果有两列名为'Field1',则返回的哈希有键'Field1_1'和'Field1_2',但是如果只有一个名为'Field3'的列,则返回的哈希中的相应键是'Field3'。

所以对于一个文件:

'Field1','Field2','Field3','Field1','Field4'
'alpha','beta','gamma','delta','kappa'

生成的哈希应该是:

$hash = {
    'Field1_1' => 'alpha',
    'Field2' => 'beta',
    'Field3' => 'gamma',
    'Field1_2' => 'delta',
    'Field4' => 'kappa',
};

2 个答案:

答案 0 :(得分:4)

问题的关键在于如何将增加的数字后缀附加到数组中的重复项。您可以将后缀添加到所有重复项,但是单个传递中的第一个是这样的:

my %seen;
my @header = qw(Field1 Field2 Field3 Field1 Field4);
my @fields = map { $seen{$_}++ > 0 ? $_ . '_' . $seen{$_} : $_ } @header;

Field1的第一次出现将保持不变,而第二次出现Field1_2。要将后缀附加到所有重复项(包括第一个),需要多次传递。这是一种方法:

my %seen;
my @header = qw(Field1 Field2 Field3 Field1 Field4);
$seen{$_}++ for @header;

# Filter out non-dupes
for (keys %seen) {
    delete $seen{$_} if $seen{$_} <= 1;
}

my @fields;
for (reverse @header) {
    if (exists $seen{$_}) {
        unshift @fields, $_ . '_' . $seen{$_}--;
    }
    else {
        unshift @fields, $_;
    }
}

__END__
Field1_1, Field2, Field3, Field1_2, Field4

使用Text::CSV,您可以使用column_names()方法指定列名称(惊喜,惊喜)。以下内容使用更简单的单遍方法中的映射列名称将CSV读入hashrefs数组:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
use Text::CSV;

my $csv = Text::CSV->new({ binary => 1, auto_diag => 1})
        or die "Cannot use CSV: " . Text::CSV->error_diag();

open my $fh, '<', 'file.csv' or die $!;

my $header = $csv->getline($fh);

my %seen;
my @fields = map { $seen{$_}++ > 0 ? $_ . '_' . $seen{$_} : $_ } @$header;

$csv->column_names(\@fields);

my $rows = $csv->getline_hr_all($fh);

print Dumper $rows;

close $fh;

输出:

$VAR1 = [ 
          { 
            'Field4' => 'kappa',
            'Field2' => 'beta',
            'Field1_2' => 'delta',
            'Field1' => 'alpha',
            'Field3' => 'gamma'
          }
        ];

答案 1 :(得分:1)

ThisSuitIsBlackNot的回答当然是彻底和有帮助的。

那就是说,我想,为了Perl可以像热瑜伽老师一样弯曲桑拿,这表明如果后缀想要附加到所有副本,这个可以在一行中完成,但知道,这很难看:

my @foo = ('123','234','345','123','456', '000', '234', '123');
@foo = map {my $idx = $_; my $val = $foo[$idx]; grep($foo[$_] eq $val, 0..$#foo) > 1 ? $foo[$_]."_".scalar(grep($foo[$_] eq $val, 0..$idx)) : $foo[$_]} 0..$#foo;
print Dumper(\@foo) . "\n";

<强>输出:

$VAR1 = [
      '123_1',
      '234_1',
      '345',
      '123_2',
      '456',
      '000',
      '234_2',
      '123_3'
    ];