Perl,将数字键哈希转换为数组
Posted
技术标签:
【中文标题】Perl,将数字键哈希转换为数组【英文标题】:Perl, convert numerically-keyed hash to array 【发布时间】:2011-02-23 20:04:45 【问题描述】:如果我在 Perl 中有一个包含完整和连续整数映射的哈希(即,从 0 到 n 的所有键都映射到某个东西,除此之外没有键),有没有办法将它转换为数组?
我知道我可以遍历键/值对并将它们放入一个新数组中,但有些东西告诉我应该有一个内置的方法来做到这一点。
【问题讨论】:
咆哮警告! 为什么每个人都想对键进行排序?没有必要,它使算法慢得多!排序很慢! runrig 的答案是这里最好的答案。如果散列稀疏,它将起作用。数组保持顺序,但它们是随机访问结构。伙计们,我们不使用链接列表! 你是对的:有一种内置的方法。请参阅Ether's answer。 :) 这个问题是列表上下文和数组之间存在差异的另一个原因。再加上切片,它可以让你在列表和哈希之间进行转换,而无需任何特殊的魔法或迭代,而恕我直言是 Perl 最强大的特性之一。 【参考方案1】:如果您的原始数据源是哈希:
# first find the max key value, if you don't already know it:
use List::Util 'max';
my $maxkey = max keys %hash;
# get all the values, in order
my @array = @hash0 .. $maxkey;
或者,如果您的原始数据源是 hashref:
my $maxkey = max keys %$hashref;
my @array = @$hashref0 .. $maxkey;
使用这个例子很容易测试:
my %hash;
@hash0 .. 9 = ('a' .. 'j');
# insert code from above, and then print the result...
use Data::Dumper;
print Dumper(\%hash);
print Dumper(\@array);
$VAR1 =
'6' => 'g',
'3' => 'd',
'7' => 'h',
'9' => 'j',
'2' => 'c',
'8' => 'i',
'1' => 'b',
'4' => 'e',
'0' => 'a',
'5' => 'f'
;
$VAR1 = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j'
];
【讨论】:
hashref 没有->
运算符?
@Zaid:不,这不是你在 hashrefs 中使用切片的方式。
愚蠢的我,它被转换为一个数组,这就是为什么不需要箭头运算符的原因。
不涉及强制转换或数组。 hash slice 返回一个list。
即使哈希键不是数字,这也能工作?【参考方案2】:
您可以使用 values
函数从哈希中提取所有值:
my @vals = values %hash;
如果您希望它们按特定顺序排列,则可以按所需顺序放置键,然后从中获取hash slice:
my @sorted_vals = @hashsort $a <=> $b keys %hash;
【讨论】:
【参考方案3】:好的,这不是很“内置”但可以工作。恕我直言,它比任何涉及“排序”的解决方案更受欢迎,因为它更快。
map $array[$_] = $hash$_ keys %hash; # Or use foreach instead of map
否则效率会降低:
my @array = map $hash$_ sort $a<=>$b keys %hash;
【讨论】:
或避免在 void 上下文中映射:$array[$_] = $hash$_ for keys %hash; 那必须是sort $a <=> $b
。请记住,sort
默认为字符串比较。【参考方案4】:
Perl 没有提供内置的方法来解决您的问题。
如果您知道密钥覆盖特定范围 0..N
,您可以利用这一事实:
my $n = keys(%hash) - 1;
my @keys_and_values = map $_ => $hash$_ 0 .. $n;
my @just_values = @hash0 .. $n;
【讨论】:
或者my $n = $#[keys %hash]
,如果你讨厌常量减法...【参考方案5】:
这将使未在%hashed_keys
中定义的键保留为undef
:
# if we're being nitpicky about when and how much memory
# is allocated for the array (for run-time optimization):
my @keys_arr = (undef) x scalar %hashed_keys;
@keys_arr[(keys %hashed_keys)] =
@hashed_keys(keys %hashed_keys);
而且,如果您使用参考:
@$keys_arr[(keys %$hashed_keys)] =
@$hashed_keys(keys %$hashed_keys);
或者,更危险的是,因为它假设你说的是真的(它可能并不总是真的......只是说!):
@keys_arr = @hashed_keys(sort $a <=> $b keys %hashed_keys);
但这有点离题了。如果它们一开始是整数索引的,为什么现在它们在散列中?
【讨论】:
善用切片。关于数据结构的初始选择也很好。太糟糕了,你提出了sort
,排序很慢(见我上面的咆哮)并且容易出错并且不可取。
@daotoad - 第一个(也是推荐的)解决方案不使用sort
。但是,我完全同意sort
;它每次调用最多生成 n 平方任意函数调用 [提示:这很糟糕]。【参考方案6】:
正如 DVK 所说,没有内置的方法,但这可以解决问题:
my @array = map $hash$_ sort $a <=> $b keys %hash;
或者这个:
my @array;
keys %hash;
while (my ($k, $v) = each %hash)
$array[$k] = $v
基准测试看看哪个更快,我猜是第二个。
【讨论】:
嗯,第一个平均为 O(NlogN),但最高为 O(N^2)。第二种方法只是 O(N)。无需基准测试。【参考方案7】:@a = @hsort $a <=> $b keys %h;
【讨论】:
【参考方案8】:结合FM 和Ether 的答案可以避免定义一个原本不必要的标量:
my @array = @hash 0 .. $#[ keys %hash ] ;
巧妙的是,与 scalar
方法不同的是,$#
即使在第一个元素的默认索引 $[
不为零的情况下也能正常工作。
当然,这意味着写一些愚蠢和混淆的东西:
my @array = @hash $[ .. $#[ keys %hash ] ; # Not recommended
但是总有远程机会有人在某处需要它(wince)...
【讨论】:
【参考方案9】:$Hash_value =
'54' => 'abc',
'55' => 'def',
'56' => 'test',
;
while (my ($key,$value) = each %$Hash_value)
print "\n $key > $value";
【讨论】:
【参考方案10】:我们可以写一段如下:
$j =0;
while(($a1,$b1)=each(%hash1))
$arr[$j][0] = $a1;
($arr[$j][1],$arr[$j][2],$arr[$j][3],$arr[$j][4],$arr[$j][5],$arr[$j][6]) = values($b1);
$j++;
$a1 包含密钥和 $b1 包含值 在上面的示例中,我有数组的哈希,并且该数组包含 6 个元素。
【讨论】:
如果原始哈希包含 100 个元素怎么办?或者如果你不知道它包含多少元素?【参考方案11】:一个简单的方法是@array = %hash
例如,
my %hash = (
"0" => "zero",
"1" => "one",
"2" => "two",
"3" => "three",
"4" => "four",
"5" => "five",
"6" => "six",
"7" => "seven",
"8" => "eight",
"9" => "nine",
"10" => "ten",
);
my @array = %hash;
print "@array";
会产生以下输出,
3 三 9 九 5 五 8 八 2 二 4 四 1 一 10 十 7 七 0 零 六六
【讨论】:
以上是关于Perl,将数字键哈希转换为数组的主要内容,如果未能解决你的问题,请参考以下文章
在 perl 中使用 TEXT::CSV_XS 模块将 CSV 文件转换为哈希结构