web-dev-qa-db-ja.com

Perlで、指定された配列からキーを取得するハッシュを作成するにはどうすればよいですか?

私が配列を持っているとしましょう、そして私は多くのことをするつもりだと知っています。「配列にはXが含まれていますか?」チェック。これを行うための効率的な方法は、その配列をハッシュに変換することです。ここで、キーは配列の要素です。

if($ hash {X}){...}

この配列からハッシュへの変換を行う簡単な方法はありますか?理想的には、匿名配列を取得して匿名ハッシュを返すのに十分な汎用性が必要です。

77
raldi
%hash = map { $_ => 1 } @array;

「@hash {@array} = ...」ソリューションほど短くはありませんが、これらはハッシュと配列を別の場所で定義する必要がありますが、これは匿名配列を取得して匿名ハッシュを返すことができます。

これは、配列内の各要素を取得し、「1」とペアにします。この(キー、1、キー、1、キー1)ペアのリストがハッシュに割り当てられると、奇数番号のものがハッシュのキーになり、偶数番号のものがそれぞれの値になります。

113
raldi
 @hash{@array} = (1) x @array;

これはハッシュスライスであり、ハッシュからの値のリストであるため、list-y @を前に付けます。

ドキュメント から:

「%」の代わりに「@」をハッシュスライスに使用する理由について混乱している場合は、このように考えてください。角かっこ(角かっこまたは中かっこ)の種類は、それが配列であるか、ハッシュが調べられているかを制御します。一方、配列またはハッシュの先頭の記号(「$」または「@」)は、特異値(スカラー)または複数値(リスト)のどちらを取得しているかを示します。

42
moritz
@hash{@keys} = undef;

@でハッシュを参照する構文は、ハッシュスライスです。基本的に$hash{$keys[0]} AND $hash{$keys[1]} AND $hash{$keys[2]} ...は、=、左辺値の左側のリストであり、そのリストに割り当てています。実際にハッシュに入り、すべての名前付きキーの値を設定します。この場合、指定した値は1つだけであるため、その値は$hash{$keys[0]}に入り、他のハッシュエントリはすべて未定義の値で自動的に有効になります(有効になります)。 [ここでの私の最初の提案は、式を1に設定することで、1つのキーを1に、他のキーをundefに設定します。一貫性を保つために変更しましたが、以下に示すように、正確な値は重要ではありません。]

=値の左側の式である左辺値がハッシュから構築されたリストであることを理解すると、その@を使用している理由を理解し始めるでしょう。 [Perl 6でこれが変わると思います。]

ここでの考え方は、ハッシュをセットとして使用しているということです。重要なのは、私が割り当てている価値ではありません。それはただのキーの存在です。だからあなたがしたいことは次のようなものではありません:

if ($hash{$key} == 1) # then key is in the hash

代わりに:

if (exists $hash{$key}) # then key is in the set

私にとって重要なことは、ハッシュのキーだけでセットを表すという概念だけですが、実際にはハッシュの値を気にするよりもexistsチェックを実行する方が効率的です。また、誰かが、ここでundefを値として使用することにより、値を割り当てるよりも少ないストレージ領域を消費することを指摘しました。 (また、値は重要ではないため、混乱が少なくなり、私のソリューションはハッシュの最初の要素にのみ値を割り当て、他の要素はundefのままにします。ハッシュに入れる値の配列;完全に無駄な努力)。

36
skiphoppy

if ( exists $hash{ key } )を入力するのがあまり面倒でない場合(関心のある問題はその値の真実性よりもキーの存在であるため、これを使用することをお勧めします)、短くて甘い

@hash{@key} = ();
16

いつも思っていた

foreach my $item (@array) { $hash{$item} = 1 }

少なくともニースで​​あり、読みやすい/保守可能でした。

7
Keith

ここには、多くの「配列にXが含まれていますか」という最も効率的な方法であるという前提があります。チェックは、配列をハッシュに変換することです。効率は乏しいリソース、多くの場合時間に依存しますが、時にはスペース、そして時にはプログラマーの努力に依存します。リストとリストのハッシュを同時に保持することにより、消費されるメモリを少なくとも2倍にします。さらに、テスト、ドキュメント化などに必要な、よりオリジナルのコードを書いています。

別の方法として、List :: MoreUtilsモジュール、特に関数any()none()true()およびfalse()を見てください。これらはすべて、map()およびgrep()と同様に、条件としてブロックを取り、引数としてリストを取ります。

print "At least one value undefined" if any { !defined($_) } @list;

クイックテストを実行し、/ usr/share/dict/wordsの半分を配列(25000ワード)にロードし、配列の両方を使用して、配列内の辞書全体(5000ワードごと)から選択された11ワードを探しました。 -to-hashメソッドとList :: MoreUtilsのany()関数。

ソースからビルドされたPerl 5.8.8では、array-to-hashメソッドはany()メソッドよりもほぼ1100倍高速です(Ubuntu 6.06のパッケージ化されたPerl 5.8.7では1300倍高速です)。

しかし、これは完全な話ではありません-配列からハッシュへの変換には約0.04秒かかります。この場合、配列からハッシュへの時間効率はany()メソッドよりも1.5x-2x速くなります。まだ良好ですが、星ほどではありません。

私の直感では、ほとんどの場合、配列からハッシュへの方法はany()に勝ると思いますが、もっと堅実なメトリック(たくさんのテストケース、まともな統計分析、各メソッドのビッグOアルゴリズム分析など)。ニーズに応じて、List :: MoreUtilsがより良いソリューションになる場合があります。確かに柔軟性が高く、コーディングも少なくて済みます。時期尚早の最適化は罪だということを忘れないでください... :)

7
arclight

Perl 5.10には、魔法に近い~~演算子があります:

sub invite_in {
    my $vampires = [ qw(Angel Darla Spike Drusilla) ];
    return ($_[0] ~~ $vampires) ? 0 : 1 ;
}

こちらをご覧ください: http://dev.Perl.org/Perl5/news/2007/Perl-5.10.0.html

5
RET

また、完全性のために注目に値する、2つの同じ長さの配列_@keys_と_@vals_を使用してこれを行う私の通常の方法はハッシュでした...

my %hash = map { $keys[$_] => $vals[$_] } ([email protected]);

5
Tamzin Blake

Raldiのソリューションは、これまで強化できます(元の '=>'は不要です)。

my %hash = map { $_,1 } @array;

この手法は、テキストリストをハッシュに変換するためにも使用できます。

my %hash = map { $_,1 } split(",",$line)

さらに、次のような値の行がある場合: "foo = 1、bar = 2、baz = 3"これを行うことができます:

my %hash = map { split("=",$_) } split(",",$line);

[含める編集]


提供される別のソリューション(2行かかります)は次のとおりです。

my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
@hash{@array} = undef;
3
Frosty

Perl6 :: Junction を使用することもできます。

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }
2
Brad Gilbert

多くの集合論的操作を行う場合- Set :: Scalar または同様のモジュールを使用することもできます。次に、$s = Set::Scalar->new( @array )がSetをビルドします-$s->contains($m)でクエリできます。

1
zby

名前空間を汚染したくない場合は、コードをサブルーチンに配置できます。

my $hash_ref =
  sub{
    my %hash;
    @hash{ @{[ qw'one two three' ]} } = undef;
    return \%hash;
  }->();

またはさらに良い:

sub keylist(@){
  my %hash;
  @hash{@_} = undef;
  return \%hash;
}

my $hash_ref = keylist qw'one two three';

# or

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

本当に配列参照を渡したい場合:

sub keylist(\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;
1
Brad Gilbert
#!/usr/bin/Perl -w

use strict;
use Data::Dumper;

my @a = qw(5 8 2 5 4 8 9);
my @b = qw(7 6 5 4 3 2 1);
my $h = {};

@{$h}{@a} = @b;

print Dumper($h);

与える(繰り返しキーは配列の最大の位置で値を取得することに注意してください-すなわち、6ではなく8> 2)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };
0
Mark Dibley

Tie :: IxHash をチェックアウトすることもできます。これは、順序付けられた連想配列を実装します。これにより、データの1つのコピーに対して両方のタイプのルックアップ(ハッシュとインデックス)を実行できます。

0
Dave G