如何在Ruby中检查一个对象是否是可迭代的?
也就是说,我想要一个方法来干净地检查一个对象是否是可迭代的,就像这样:
def is_iterable(my_object)
..
end
除非在方法内部明确命名类,否则我真的不知道从哪里开始。
编辑: 对于我的目的,假设可迭代对象是可以使用 .each 方法进行操作的。
如何在Ruby中检查一个对象是否是可迭代的?
也就是说,我想要一个方法来干净地检查一个对象是否是可迭代的,就像这样:
def is_iterable(my_object)
..
end
除非在方法内部明确命名类,否则我真的不知道从哪里开始。
编辑: 对于我的目的,假设可迭代对象是可以使用 .each 方法进行操作的。
虽然您已经得到了一些答案,但这里有两种更多的方法:Object#is_a?/Object#kind_of?和 Module#=== :
[].is_a? Enumerable #=> true
"".is_a? Enumerable #=> false
Enumerable === [] #=> true
Enumerable === "" #=> false
Hash
也是一个Enumerable
,但在大多数情况下不应该被识别为集合。 - Wile E.Enumberable
基本上就是这个定义。HashMap
称为集合,.Net的Dictionary
定义在System.Collections.Generic
中,因此断言它们不被认为是“集合”似乎是武断的。Hash
的定义。 - Michael Kohl就我使用的情况而言,可迭代对象是指您可以对其执行
.each
操作的东西。
您只需询问此对象是否具有此方法即可。
def iterable?(object)
object.respond_to?(:each)
end
nil..nil
是一个范围对象,可以响应each
方法,但无法迭代,当尝试进行迭代时会抛出TypeError: can't iterate from NilClass
异常。我在一个日期范围输入项目中遇到了这个问题,测试其是否与无效的用户输入(如空值或随机字符串等)相匹配。 - Vance Lucas根据您的整体目标和需要对结果进行的操作,有许多方法可以实现这一点。
If you just want to use duck typing to see if the object responds to #each, then you can just ask the object if it has such a method.
my_object.respond_to? :each
If you want to find out if the object mixes in the Enumerable class, you can check the class for inclusion.
my_object.class.include? Enumerable
If you want a list of all the ancestors and mixins, you want the #ancestors method. For example, to see whether my_object inherits from the Enumerable class, you could invoke:
my_object = []
my_object.class.ancestors
=> [Array, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
my_object.class.ancestors.include? Enumerable
=> true
my_object.class.include? Enumerable
而不是 my_object.is_a? Enumerable
? - dbenhurmy_object.class < Enumerator
。 - mbillard由于Range
即使不可迭代也会响应each
,因此您需要特别检查Range的元素是否响应succ
def iterable?(object)
return object.begin.respond_to? :succ if object.kind_of? Range
object.respond_to? :each
end
each
方法,或者对象的类中是否包含Enumerable
模块:my_object.class.include? Enumerable
Minitest
风格的测试用例,用于ActiveRecord
组合对象:
def test_iterates_over_invoice_items
invoice = Invoice.new(items: [InvoiceItem.new(description: 'test')])
iteration_count = 0
invoice.each do |invoice_item|
assert_equal 'test', invoice_item.description
iteration_count += 1
end
assert_equal 1, iteration_count
end