从Hash of Arrays中读取列

时间:2012-10-17 14:45:37

标签: perl hash perl-data-structures

我是perl的新手,并且有一个关于使用数组哈希来检索特定列的问题。我的代码如下:

my %hash = ( name1 => ['A', 'A', 'B', 'A', 'A', 'B'],
             name2 => ['A', 'A', 'D', 'A', 'A', 'B'],
             name3 => ['A', 'A', 'B', 'A', 'A', 'C'],
             );

#the values of %hash are returned as arrays not as string (as I want)

foreach my $name (sort keys %hash ) {
    print "$name: ";
    print "$hash{$name}[2]\n";
}

for (my $i=0; $i<$length; $i++) {
        my $diff = "no";
        my $letter = '';
        foreach $name (sort keys %hash) {
            if (defined $hash{$name}[$i]) {
                if ($hash{$name}[$i] =~ /[ABCD]/) {
                    $letter = $hash{$name}[$i];
                }
                elsif ($hash{$name}[$i] ne $letter) { 
                    $diff = "yes";
                }
            }
            if ( $diff eq "yes" ) {
                foreach $name (sort keys %hash) {
                    if (defined $hash{$name}[$i]) { $newhash{$name} .= $hash{$name}[$i]; }  
                }
            }
        }
    }
    foreach $name (sort keys %newhash ) {
        print "$name: $newhash{$name} \n";
    }

我希望这个程序的输出类似于只有变量列的新哈希:

my %newhash = ( name1 => 'BB',
            name2 => 'DB',
            name3 => 'BC',
              );

但仅提供此消息: 在test_hash.pl第31行的字符串ne中使用未初始化的值$ letter。

有没有人有这方面的想法? 干杯

编辑:

非常感谢你对这个问题的帮助。

我编辑了我的帖子以确认frezik,Dan1111,Jean的建议。你是对的,现在没有警告,但我也不能从print语句得到任何输出,我也没有任何关于这个的线索......

@TLP:好的,我只是生成一组随机的列,没有任何订单目的。我真正想要的是关于字母如何变化,这意味着如果对于相同的数组索引(存储在散列中)字母是相同的,丢弃那些,但如果字母在键之间是不同的,我想存储该索引新哈希中的列。

干杯。

6 个答案:

答案 0 :(得分:2)

我认为通过这个,你想要匹配任何字母A,B,C或D:

if ($hash{$name}[$i] =~ /ABCD/)

但是,如上所述,它与确切的字符串“ABCD”匹配。你需要一个你想要的角色类:

if ($hash{$name}[$i] =~ /[ABCD]/)

但是,您还有其他逻辑问题,这可能导致您在设置之前与$letter进行比较。将其设置为空(如Jean建议的那样)是一个可能有用的简单选项。

另一个问题是:

print "$name: @{ $newhash{$name} }\n";

%newhash不是数组的哈希值,因此您需要删除数组解除引用:

print "$name: $newhash{$name} \n";

答案 1 :(得分:2)

您可能对此替代解决方案感兴趣

use strict;
use warnings;

my %hash = (
  name1 => ['A', 'A', 'B', 'A', 'A', 'B'],
  name2 => ['A', 'A', 'D', 'A', 'A', 'B'],
  name3 => ['A', 'A', 'B', 'A', 'A', 'C'],
);

my @columns;

for my $list (values %hash) {
  $columns[$_]{$list->[$_]}++ for 0 .. $#$list;
}

my %newhash = %hash;

for my $list (values %newhash) {
  $list = join '', map $list->[$_], grep keys %{$columns[$_]} > 1, 0 .. $#$list;
}

use Data::Dump;
dd \%newhash;

<强>输出

{ name1 => "BB", name2 => "DB", name3 => "BC" }

答案 2 :(得分:1)

您的标量$letter未定义。添加此项以消除警告。

my $letter='';

答案 3 :(得分:1)

if ($hash{$name}[$i] =~ /ABCD/) {

上面的正则表达式会匹配__ABCD__ABCD1234这样的字符串,但绝不会匹配AB。你可能想要匹配这些字母中的任何一个,并且固定正则表达式也是个好主意:

if ($hash{$name}[$i] =~ /\A [ABCD] \z/x) {

(/ x选项意味着忽略空白,这有助于使正则表达式更容易阅读。)

$i == 2和内部循环首先碰到按键name1name3时,您仍会在上面的示例中收到警告。由于正则表达式与T不匹配,$letter将保持未初始化状态。

答案 4 :(得分:1)

我认为一个一个地检查字母是错误的。收集所有字母并立即检查它们似乎更容易。然后List::MoreUtils模块的uniq函数可以快速确定字母是否变化,并且可以轻松地将它们转换为结果散列。

use strict;
use warnings;
use Data::Dumper;
use List::MoreUtils qw(uniq);

my %hash = ( name1 => ['A', 'A', 'B', 'A', 'A', 'B'],
             name2 => ['A', 'A', 'D', 'A', 'A', 'B'],
             name3 => ['A', 'A', 'B', 'A', 'A', 'C'],
);
my @keys = keys %hash;
my $len = $#{ $hash{$keys[0]} };   # max index
my %new;

for my $i (0 .. $len) {
    my @col;
    for my $key (@keys) {
        push @col, $hash{$key}[$i];
    }
    if (uniq(@col) != 1) {     # check for variation
        for (0 .. $#col) {
            $new{$keys[$_]} .= $col[$_];
        }
    }
}
print Dumper \%new;

<强>输出:

$VAR1 = {
          'name2' => 'DB',
          'name1' => 'BB',
          'name3' => 'BC'
        };

答案 5 :(得分:0)

大。非常感谢你在这个问题上的所有帮助。

我根据TLP的建议尝试了一个代码并且运行得很好。因为我在perl中相对较新,我认为这段代码比Borodin的代码更容易理解。我做的是:

#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw(uniq);

my %hash = ( name1 => ['A', 'A', 'T', 'A', 'A', 'T', 'N', 'd', 'd', 'D', 'C', 'T', 'T', 'T'],
         name2 => ['A', 'A', 'D', 'A', 'A', 'T', 'A', 'd', 'a', 'd', 'd', 'T', 'T', 'C'],
         name3 => ['A', 'A', 'T', 'A', 'A', 'C', 'A', 'd', 'd', 'D', 'C', 'T', 'C', 'T'],
);
my @keys = keys %hash;
my $len = $#{ $hash{$keys[0]} };   # max index
my %new;

for (my $i=0; $i<$length; $i++) {
    my @col;
    for my $key (@keys) {
       if ($hash{$key}[$i] =~ /[ABCDT]/) {     #added a pattern match
            push @col, $hash{$key}[$i];
       }
    }
    if (uniq(@col) != 1) {     # check for variation
        for (0 .. $#col) {
            $new{$keys[$_]} .= $col[$_];
        }
    }
}
foreach my $key (sort keys %new ) {
    print "$key: $new{$key}\n";
}

然而,当玩uniq函数时(if(uniq(@col)== 1)),我注意到输出有点儿错误:

name1: AAAAADCT
name2: AAAAADCT
name3: AAAAT

似乎没有保留密钥的初始顺序=&gt;值。有人对此有暗示吗?

干杯。