อะไรคือวิธีที่สง่างามใน Ruby เพื่อบอกว่าตัวแปรคือ Hash หรือ Array?


144

เพื่อตรวจสอบว่า@some_varคืออะไรฉันกำลังทำไฟล์

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

ผมมั่นใจว่ามีวิธีที่สง่างามมากขึ้นเพื่อตรวจสอบว่า@some_varเป็นหรือHashArray


1
Andrew: ฉันกำลังเรียก API และใน JSON ฉันได้รับกลับมาหากมีหลายผลลัพธ์ฉันจะได้รับ Array แต่ถ้ามีเพียงอันเดียวฉันจะได้รับ Hash แทนที่จะเป็น Array องค์ประกอบเดียว มีการส่งต่อที่ดีกว่าการตรวจสอบ Array vs Hash หรือไม่?
drhyde

2
คุณจะได้รับอย่างใดอย่างหนึ่ง[result_hash, another_result_hash]หรือsingle_result_hash? ใครก็ตามที่สร้าง API นั้นทำงานได้ไม่ดี!
Andrew Grimm

2
คุณพูดถูกแอนดรูว์และฉันพนันได้เลยว่าการให้คนที่เขียน API แก้ไขมันง่ายกว่าการทดสอบแฮชกับอาร์เรย์
Olivier 'Ölbaum' Scherler

ฉันมีสถานการณ์เช่นเดียวกับ @drhyde ในกรณีของฉันบุคคลที่สามคือ HTTParty ซึ่งหลังจากแยกวิเคราะห์ไฟล์ XML ไม่มีวิธีใดที่จะตัดสินใจได้ว่าวิธีใดเป็นวิธีที่ดีที่สุดในการจัดการสถานการณ์ที่องค์ประกอบสามารถมีลูกได้ 1-n
Dirty Henry

คุณควรดู Avdi Grimms screencast: rubytapas.com/2016/10/17/episode-451-advanced-class-membershipและอาจทำให้ cabos ตอบได้ว่าเป็นที่ยอมรับ
Alexander Presber

คำตอบ:


271

คุณสามารถทำได้:

@some_var.class == Hash

หรือสิ่งที่ชอบ:

@some_var.is_a?(Hash)

เป็นที่น่าสังเกตว่า "is_a?" เมธอดเป็นจริงถ้าคลาสอยู่ที่ใดก็ได้ในแผนผังบรรพบุรุษของอ็อบเจ็กต์ ตัวอย่างเช่น:

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

ข้างต้นเป็นจริงถ้า @some_var เป็นอินสแตนซ์ของแฮชหรือคลาสอื่น ๆ ที่เกิดจาก Object ดังนั้นหากคุณต้องการจับคู่ประเภทคลาสอย่างเข้มงวดโดยใช้ == หรือ instance_of? วิธีการอาจเป็นสิ่งที่คุณกำลังมองหา


20
is_a?เป็นตัวเลือกที่ดีที่สุดเนื่องจากจะส่งคืนtrueสำหรับคลาสย่อยด้วย
Fábio Batista

และแน่นอนคุณทิ้งวงเล็บไว้@som_var.is_a? Hashด้วยก็ใช้ได้เช่นกัน :)
Gus Shortz

7
โปรดระวังสิ่งนี้อาจทำให้คุณเสียหายได้หากมีคนส่งอินสแตนซ์ ActiveSupport :: HashWithIndifferentAccess ให้คุณ ซึ่งตอบสนองเหมือนแฮช แต่แท้จริงแล้วไม่ใช่แฮช :(
unflores

ฉันชอบคำตอบเพิ่มเติมเกี่ยวกับรายละเอียดเกี่ยวกับการพิมพ์เป็ดเนื่องจากผู้เขียนต้นฉบับเห็นได้ชัดว่ากำลังมองหาวิธีตรวจสอบประเภททับทิม
NobodysNightmare

3
@unflores ActiveSupport::HashWithIndifferentAccessสืบทอดมาจาก Hash ดังนั้น@some_var.is_a?(Hash)จะกลับมาเป็นจริงในกรณีนี้เช่นกัน (ฉันเพิ่งมีคำถามเดียวกันและกรณี 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)

คุณสามารถวนซ้ำได้ที่ @some_var เพราะมันตอบสนองต่อ: each

หากคุณต้องการทราบประเภทจริงๆและถ้าเป็น Hash หรือ Array คุณสามารถทำได้:

["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) ลำดับขององค์ประกอบถูกนำไปใช้แตกต่างกันระหว่างแฮชและอาร์เรย์และมีความแตกต่างกันระหว่างรุ่นทับทิม ( intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid

1
@ juan2raid: ถ้าออร์เดอร์สำคัญโทรsortมาก่อน
Andrew Grimm

@Brandon ตัวอย่างที่สองควรแปลงคลาสเป็นสตริง <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON


4

ฉันใช้สิ่งนี้:

@var.respond_to?(:keys)

ใช้งานได้กับ Hash และ ActiveSupport :: HashWithIndifferentAccess


4

ในทางปฏิบัติคุณมักจะต้องการดำเนินการที่แตกต่างกันขึ้นอยู่กับว่าตัวแปรนั้นเป็น Array หรือ Hash ไม่ใช่แค่บอกเท่านั้น ในสถานการณ์เช่นนี้สำนวนที่สวยงามมีดังต่อไปนี้:

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
Tom Lord

0

หากคุณต้องการทดสอบว่าวัตถุนั้นเคร่งครัดหรือขยาย a 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.