Rubyで変数がハッシュか配列かを判別するエレガントな方法は何ですか?


140

何をチェックするために@some_var、私はやっています

if @some_var.class.to_s == 'Hash' 

私がいるかどうかを確認するために、よりエレガントな方法があると確信して@some_varいますHashArray


1
Andrew:私はAPIを呼び出しており、JSONで返されます。複数の結果がある場合は配列を取得しますが、1つしかない場合は単一の要素の配列ではなくハッシュを取得します。配列とハッシュのチェックよりも良い方法はありますか?
drhyde

2
いずれかを取得あなたはそう[result_hash, another_result_hash]single_result_hash?そのAPIを作成した人は誰もうまくいきませんでした!
アンドリューグリム

2
Andrew、そのとおりです。APIを作成した人に修正を依頼する方が、ハッシュと配列を比較するよりもはるかに簡単です。
Olivier 'Ölbaum' Scherler

@drhydeと同じ状況です。私の場合、サードパーティはHTTPartyであり、XMLファイルの解析後、要素が1-nの子を持つことができる状況を処理するための最良の方法を決定する方法がありません。
Dirty Henry

Avdi Grimmsのスクリーンキャストをご覧ください:rubytapas.com/2016/10/17/episode-451-advanced-class-membershipおそらく、カボに受け入れられたものに回答してもらいます。
Alexander Presber

回答:


261

あなたはただ行うことができます:

@some_var.class == Hash

または次のようなもの:

@some_var.is_a?(Hash)

「is_a?」に注意してください。クラスがオブジェクト祖先ツリーのどこかにある場合、メソッドはtrueです。例えば:

@some_var.is_a?(Object)  # => true

上記は、@ some_varがObjectから派生したハッシュまたはその他のクラスのインスタンスである場合に当てはまります。したがって、クラスタイプを厳密に一致させる場合は、==またはinstance_of?メソッドはおそらくあなたが探しているものです。


20
is_a?trueサブクラスに対しても返されるため、これは最良のオプションです。
ファビオバティスタ

そして、もちろん、あなたも括弧を@som_var.is_a? Hash
省く

7
誰かがあなたにActiveSupport :: HashWithIndifferentAccessのインスタンスを渡してしまうと、これは本当にあなたを台無しにするかもしれないことに注意してください。これはハッシュのように応答しますが、実際にはハッシュではありません。:(
unflores 2014年

元の作者は明らかに型チェックを行うルビーの方法を探していたので、アヒルのタイピングに関して詳細に行ってもっと多くの回答を求めていました。
NobodysNightmare 2015

3
@unflores ActiveSupport::HashWithIndifferentAccessはHashを継承しているため@some_var.is_a?(Hash)、この場合もtrueを返します。(私は同じ質問とHashWithIndifferentAccessケースを持っていて、コメントから少し混乱しました...それで明確にしたいと思いました)
Stilzk1n

38

まず、文字通りの質問に対する最良の答えは

Hash === @some_var

しかし、アヒルのタイピングを行う方法をここに示すことによって、質問は本当に答えられるべきでした。それはあなたが必要とするアヒルの種類に少し依存します。

@some_var.respond_to?(:each_pair)

または

@some_var.respond_to?(:has_key?)

あるいは

@some_var.respond_to?(:to_hash)

用途によっては正しい場合があります。


25

通常、ルビーでは、「タイプ」を探しているときに、「アヒルのタイプ」または「カモのようにいかがですか」が実際に必要です。特定のメソッドに応答するかどうかを確認します。

@some_var.respond_to?(:each)

:eachに応答するので、@ some_varを反復できます。

タイプを本当に知りたい場合、それがハッシュまたは配列の場合は、次のようにすることができます。

["Hash", "Array"].include?(@some_var.class)  #=> check both through instance class
@some_var.kind_of?(Hash)    #=> to check each at once
@some_var.is_a?(Array)   #=> same as kind_of

これは、コードがデータの順序に依存している場合(たとえば、each_with_indexを使用している場合)は機能しません。要素の順序は、ハッシュと配列間で実装が異なり、Rubyのバージョン間でも異なります。(intertwingly.net/slides/2008/oscon/ruby19/22
juan2raid

1
@ juan2raid:順序が重要な場合は、sort最初にそれを呼び出します。
Andrew Grimm

@Brandonの2番目の例では、クラスを文字列に変換します。<br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON

12
Hash === @some_var #=> return Boolean

これは、caseステートメントでも使用できます

case @some_var
when Hash
   ...
when Array
   ...
end

4

私はこれを使います:

@var.respond_to?(:keys)

HashおよびActiveSupport :: HashWithIndifferentAccessで機能します。


4

実際には、変数が単なる指示ではなく、配列であるかハッシュであるかに応じて、異なる方法で動作することがよくあります。この場合、エレガントなイディオムは次のとおりです。

case item
  when Array
   #do something
  when Hash
   #do something else
end

.classメソッドを呼び出さないことに注意してくださいitem


2

使用できます instance_of?

例えば

@some_var.instance_of?(Hash)

これはサブクラスでは機能しないことに注意してください!あなたが持っている場合たとえば、ActiveRecord::Collectionとしてみてくださいinstance_of?(Array)、これが返されますfalse一方、is_a?(Array)戻りますtrue
トム・ロード

0

オブジェクトが厳密にまたは拡張されているかどうかをテストする場合はHash、次を使用します。

value = {}
value.is_a?(Hash) || value.is_a?(Array) #=> true

しかし、Rubyのダックタイピングの価値を高めるには、次のようにします。

value = {}
value.respond_to?(:[]) #=> true

value[:key]構文を使用して特定の値にのみアクセスする場合に便利です。

発生Array.new["key"]することに注意してくださいTypeError


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.