36

如何检查对象在 Ruby 中是否可迭代?

也就是说,我想要一个干净地检查对象是否可迭代的方法,如下所示:

def is_iterable(my_object)
  ..
end

我真的不确定从方法中显式命名类的不足从哪里开始。

编辑: 出于我的目的,假设 iterable 是你可以做的事情 .each 。

4

6 回答 6

47

就我的目的而言,假设可迭代是您可以做的事情.each

你可以问这个对象是否有这个方法

def iterable?(object)
  object.respond_to?(:each)
end
于 2012-05-25T14:59:07.213 回答
46

您已经得到了一些答案,但这里还有另外两种方法,Object#is_a?/Object#kind_of? 模块#===

  [].is_a? Enumerable #=> true
  "".is_a? Enumerable #=> false

  Enumerable === [] #=> true
  Enumerable === "" #=> false
于 2012-05-25T15:23:19.810 回答
10

有多种方法可以做到这一点,具体取决于您的更大目标以及您需要对结果做什么。

  1. 如果您只想使用鸭子类型来查看对象是否响应#each,那么您可以只询问对象是否有这样的方法。

    my_object.respond_to? :each
    
  2. 如果您想了解对象是否混合在 Enumerable 类中,您可以检查该类是否包含。

    my_object.class.include? Enumerable
    
  3. 如果你想要所有祖先和 mixin 的列表,你需要#ancestors方法。例如,要查看my_object是否继承自 Enumerable 类,您可以调用:

    my_object = []
    my_object.class.ancestors
    => [Array, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
    
    my_object.class.ancestors.include? Enumerable
    => true
    
于 2012-05-25T15:00:37.730 回答
2

因为即使在不可迭代时也会Range响应,所以您需要专门检查 Range 的元素是否响应eachsucc

def iterable?(object)
  return object.begin.respond_to? :succ if object.kind_of? Range
  object.respond_to? :each
end
于 2014-08-26T00:13:17.857 回答
1

在一般情况下,您可以检查是否each定义了方法,或者Enumerable模块是否包含在对象的类中:

my_object.class.include? Enumerable
于 2012-05-25T15:02:24.623 回答
0

MinitestActiveRecord-组合对象的样式测试用例:

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
于 2018-10-11T22:22:35.240 回答