从数组哈希中读取列



我是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',
);

但只得到以下消息: 在第 31 行test_hash.pl字符串 ne 中使用未初始化的值$letter。

有人对此有想法吗? 干杯

编辑:

非常感谢您在这个问题上的帮助。

我编辑了我的帖子以确认 frezik、Dan1111、Jean 的建议。你是对的,现在没有警告,但我也无法从 print 语句中获得任何输出,我对此一无所知......

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

干杯。

我假设通过这个,你想匹配任何字母 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";

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

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" }

我认为逐个检查字母是错误的。收集所有信件并立即检查它们似乎更容易。然后,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'
};

未定义标量$letter。添加此内容以摆脱警告。

my $letter='';
if ($hash{$name}[$i] =~ /ABCD/) {

上面的正则表达式将匹配像__ABCD__ABCD1234这样的字符串,但永远不会匹配一个单独的AB。 您可能想要匹配这些字母中的任何一个,锚定正则表达式也是一个好主意:

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

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

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

太好了。非常感谢您在这个问题上的所有帮助。

我根据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

似乎没有保留键=>值的初始顺序。有人对此有提示吗?

干杯。

最新更新