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 &lt;=&gt; $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 文件转换为哈希结构

将数组值转换为哈希键

哈希表

在 Perl 中使用 XML::Simple 将哈希转换为 XML 后内容丢失

在 Clojure 中将元组数组转换为哈希映射

在Ruby中将嵌套哈希键从CamelCase转换为snake_case