【问题标题】:substitute hash key with a different name [duplicate]用不同的名称替换哈希键[重复]
【发布时间】:2012-02-24 12:26:48
【问题描述】:

可能重复:
How to replace a Perl hash key?
Changing keys in a “for ( keys %hash ) {}”-loop

我有一个像下面这样的哈希

test0 => '1'
test1 => '2'

我想把它变成

something0 => '1'
something1 => '2'

基本上,我想从哈希中取出每个键,用“某物”替换“测试”,然后将其放回原始哈希中。

【问题讨论】:

  • 更改多个键需要特别注意。这甚至不是链接问题的完全重复。 (不知道exact是什么意思???)
  • 我会说,问题不在于如何改造,而在于为什么要首先改造。
  • @ikegami :这不是唯一的例子。以stackoverflow.com/q/2431637/133939 为例。

标签: perl


【解决方案1】:

这适用于任何 1:1 映射,即使某些键是相同的。这会将 a 切换为 b(反之亦然):

my @new_keys = keys %hash;
y/ab/ba/ foreach @new_keys;
@hash{@new_keys} = delete @hash{keys %hash};

诀窍是最后一行,它使用哈希切片同时替换所有键,因此冲突无关紧要。

编辑:基准(现在工作,感谢@EricStrom)

这里是@ikegami 发布的方法的基准(修正了拼写错误)以及我上面的方法:

                Rate  ikegami_map ikegami_5.14  ikegami_for     derobert
ikegami_map  38186/s           --         -16%         -19%         -34%
ikegami_5.14 45547/s          19%           --          -3%         -22%
ikegami_for  47065/s          23%           3%           --         -19%
derobert     58213/s          52%          28%          24%           --

基准代码:

use List::MoreUtils qw(zip);
use Benchmark qw(cmpthese);
use v5.14;
use warnings qw(all);

our %hash = zip @{[ 'a'..'z' ]}, @{[ 1..26 ]};
%hash = map { y/a-z/n-za-m/r => $hash{$_} } keys(%hash);

cmpthese(-2, {
    'derobert' => q{
        my @new_keys = keys %hash;
        y/a-z/n-za-m/ foreach @new_keys;
        @hash{@new_keys} = delete @hash{keys %hash};
    },
    'ikegami_for' => q{
        my %new_hash;
        for my $orig (keys(%hash)) {
            (my $new = $orig) =~ y/a-z/n-za-m/;
            $new_hash{$new} = $hash{$orig};
        }
        %hash = %new_hash;
    },
    'ikegami_map' => q{
        %hash = map {
               (my $new = $_) =~ y/a-z/n-za-m/;
               $new => $hash{$_}
            } keys(%hash);
    },
    'ikegami_5.14' => q{
        %hash = map { y/a-z/n-za-m/r => $hash{$_} } keys(%hash);
    },
});

【讨论】:

  • 附言。这是一个保留任何不会单独更改的键的变体:my @new_keys = my @old_keys = grep tr/ab//, keys %hash; tr/ab/ba/ foreach @new_keys; @hash{@new_keys} = delete @hash{@old_keys};
  • 您的基准测试未按您预期的方式运行。没有一个基准例程真正看到%hash 词法,相反,它们都看到了%main::hash 全局,它是空的。这可以通过在每个测试中将q{ 更改为sub{ 来解决。此外,您不应该在基准测试中修改源数据集,否则可能会影响其他测试的结果。每个人都应该首先创建一个哈希副本。
  • @EricStrom:呃,你说得对。它让我的代码看起来很糟糕:-(。至于修改基准中的数据,它在键上执行 rot13,而 rot13 是它自己的逆操作——所以它只是在状态 A 和状态 B 之间反复翻转。 '看起来应该不会丢掉任何东西(与正在执行的操作相比,副本相当昂贵)
【解决方案2】:

使用for(假设每个新密钥都不同于每个现有密钥):

for my $orig (keys(%hash)) {
    (my $new = $orig) =~ s/^test/something/;
    $hash{$new} = delete($hash{$orig});
}

使用for(任何替换都可以安全使用):

my %new_hash;
for my $orig (keys(%hash)) {
    (my $new = $orig) =~ s/^test/something/;
    $new_hash{$new} = $hash{$orig};
}
%hash = %new_hash;

使用map(任何替换都可以安全使用):

%hash = map {
       (my $new = $_) =~ s/^test/something/;
       $new => $hash{$_}
    } keys(%hash);

使用 map 和 5.14+(可安全使用任何替换):

%hash = map { s/^test/something/r => $hash{$_} } keys(%hash);

【讨论】:

  • 您的 map 方法因错字而无法使用,我将为您修正。实际上,它们都有相同的错字。实际上,我正在尝试对它们进行基准测试,请在发布之前测试您的代码。错字填充的代码让初学者感到困惑!
【解决方案3】:
my @keys = keys %hash ;
foreach my $key ( @keys ) {
  my ( $newkey = $key ) =~ s/^test/something/ ;
  $hash{$newkey} = $hash{$key} ;
  delete $hash{$key} ;
}

可能有更短的版本,但这应该可以。

【讨论】:

  • 你可以通过$hash{$newkey} = delete $hash{$key};节省一些字符
  • 它假定每个新密钥都不同于每个现有密钥,但这可能是一个安全的假设。
  • @ikegami 是的,我本可以添加一些检查,但我想把它留给 OP ;)
  • $hash{$newkey} = $hash{$key} ;delete $hash{$key} ; 可以合并为一行:$hash{$newkey} = delete $hash{$key} ;
  • @gpojd 是的,弗里多已经提到了。
猜你喜欢
  • 1970-01-01
  • 2010-12-02
  • 2014-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-03
  • 2018-06-18
  • 2013-08-26
相关资源
最近更新 更多