散列中的键映射 - if,exists,map

时间:2014-05-02 11:24:22

标签: perl if-statement hash map hash-of-hashes

我正在尝试将文件的内容与散列哈希进行比较。为此,我使用mapifexists,但效果不佳。

基本上,我想知道如果文件的第0列到第2列存在于哈希中。如果是这样,那么我想找出第3列是否作为内部哈希中的键存在。我的“旧文件.txt”是一个制表符分隔文件,我从中生成以下哈希:

old file.txt:

A    s.av    u
B    s.bv    u
C    s.av    u
C    s.cv    m

哈希:

my %oldhash = {
  'A' => {'s.av' => ['u']},
  'B' => {'s.bv' => ['u']},
  'C' => {'s.av' => ['u'], 's.cv' => ['m']},
};

查看哈希中是否存在以下与“new file.txt”相关的制表符分隔列:

D    Db    Dc    s.av   #cols 0 - 2 do not exist in hash
E    A     Ab    d.ef   #column 1 exists, but column 3 doesn't, so nothing is done
E    A     Ac    s.av   #col 1 and 3 exist, so the new file will have the value of $oldhash{A}{s.av}
B    Bb    B     s.bv   #col0 and 3 exist, so I'll include the value of $oldhash{B}{s.bv}

请注意,cols 0和2都存在于散列中,但这并不重要,因为我只需要存在一个列。

输出可以与测试文件完全一样,添加的列从另一个文件中获取um。例如:

D    Db    Dc    s.av       #inserts empty column
E    A     Ab    d.ef       #inserts empty column
E    A     Ac    s.av   u   #with u inserted
B    Bb    B     s.bv   u   #with u inserted

这是我到目前为止的地方,但我得到的是exists argument is not a HASH or ARRAY element or a subroutine at myfile.pl line 24

#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;

my $dir='D:\\';

open my $oldfile, "<", "$dir\\old file.txt";
open my $newfile, "<", "$dir\\new file.txt";

my (%oldhash);

# creates the hash of hashes
while (my $odata = <$oldfile>){
    chomp $odata;
    my @oline = split /\t/, lc $odata;
    push @{$oldhash{$oline[1]}{$oline[2]}}, $oline[3];
}

# does the comparison between the file and the hash
while (my $newlines = <$newfile>){
    chomp $newlines;
    my @line = split /\t/, $newlines;
    if (exists map {$oldhash{$_}} @line[0..2]) {
        print $oldhash{$_};
    }
}

close $updatedtax;
close $oldtax;

我感谢你能给我的所有帮助!提前谢谢

1 个答案:

答案 0 :(得分:3)

exists需要一个数组或哈希元素作为其参数。您已经传递了一个标量值列表,这些标量值的来源在经过map后就已丢失。

可以将您的测试编写为

if ( grep { exists $oldhash{$_} }, @line[0..2] ) { ... }

但我认为有更好的方法来编写解决方案。

认为这可以满足您的需求,但是根据您提供的数据,它只输出u两次。您还没有按照我的要求显示所需的输出,这是正确的吗?

我已将您为自己%oldhash选择的密钥反转,以便只需检查是否存在第四列(s.av等)即可立即拒绝案例哈希。

我还添加了use autodie作为必需来检查open是否已经成功,然后再继续使用文件句柄,这避免了检查每个案例的明确性。

最后,我添加了chdir 'D:\\',这样您就不必在文件名前加上open的路径。

输出包括最终的&#34;评论&#34;来自new_file.txt的列引起了它。我相信你可以改变print语句来提供你想要的输出。

use strict;
use warnings;
use autodie;

use Data::Dump;

chdir 'D:\\';

open my $old_fh, '<', 'old_file.txt';

my %old_data;
while (<$old_fh>) {
  chomp;
  my @fields = split /\t/;
  $old_data{$fields[1]}{$fields[0]} = $fields[2];
  print "@fields\n";
}
close $old_fh;

open my $new_fh, '<', 'new_file.txt';

while (<$new_fh>) {

  chomp;
  my @fields = split /\t/;

  my $new = '';
  if (my $list = $old_data{$fields[3]}) {
    my @possible = grep defined, @{$list}{@fields[0,1,2]};
    $new = $possible[0] if @possible;
  }

  print join("\t", @fields[0..3], $new, $fields[4]), "\n";
}

阅读文件后%old_data的内容如下所示

(
  "s.av" => { A => "u", C => "u" },
  "s.bv" => { B => "u" },
  "s.cv" => { C => "m" },
)

<强>输出

D Db  Dc  s.av    #cols 0 - 2 do not exist in hash
E A Ab  d.ef    #column 1 exists, but column 3 doesn't, so nothing is done
E A Ac  s.av  u #col 1 and 3 exist, so the new file will have the value of $oldhash{A}{s.av}
B Bb  B s.bv  u #col0 and 3 exist, so I'll include the value of $oldhash{B}{s.bv}