【问题标题】:Perl, convert numerically-keyed hash to arrayPerl,将数字键哈希转换为数组
【发布时间】:2011-02-23 20:04:45
【问题描述】:

如果我在 Perl 中有一个包含完整和连续整数映射的哈希(即,从 0 到 n 的所有键都映射到某个东西,除此之外没有键),有没有办法将它转换为数组?

我知道我可以遍历键/值对并将它们放入一个新数组中,但有些东西告诉我应该有一个内置的方法来做到这一点。

【问题讨论】:

  • 咆哮警告! 为什么每个人都想对键进行排序?没有必要,它使算法慢得多!排序很慢! runrig 的答案是这里最好的答案。如果散列稀疏,它将起作用。数组保持顺序,但它们是随机访问结构。伙计们,我们不使用链接列表!
  • 你是对的:有一种内置的方法。请参阅Ether's answer。 :) 这个问题是列表上下文和数组之间存在差异的另一个原因。再加上切片,它可以让你在列表和哈希之间进行转换,而无需任何特殊的魔法或迭代,而恕我直言是 Perl 最强大的特性之一。

标签: perl hash arrays


【解决方案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 = @hash{0 .. $maxkey};

或者,如果您的原始数据源是 hashref:

my $maxkey = max keys %$hashref;
my @array = @{$hashref}{0 .. $maxkey};

使用这个例子很容易测试:

my %hash;
@hash{0 .. 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 = @hash{sort { $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     = @hash{0 .. $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 = @h{sort { $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 零 六六

          【讨论】:

            猜你喜欢
            • 2014-07-23
            • 1970-01-01
            • 2012-07-15
            • 2012-10-29
            • 2019-10-27
            • 2019-05-23
            • 2015-04-19
            • 2015-07-10
            • 1970-01-01
            相关资源
            最近更新 更多