是否有更多的惯用ruby来执行此迭代?

时间:2015-03-20 22:03:02

标签: ruby refactoring iteration

我正在迭代一组id并希望返回第一个在调用谓词方法时返回true的对象。几行代码值得千言万语:

def applicable_question(question_ids)
  ordered_ids(question_ids, order).detect do |question_id|
    question = Question.find_by(id: question_id)
    return question if question.applicable_for?(self)
  end
end

剥离域名条款:

def desired_thing(ids)
  ids.detect do |id|
    thing = Thing.new(id)
    return thing if thing.true?
  end
end

这里有更惯用的方法吗?具体来说,我觉得我在滥用detect。我立即联系了eachbreak,但这种做法并没有走得太远。

此代码的要求是不需要实例化大量对象(例如ActiveRecord子类型)来查找所需的内容。

3 个答案:

答案 0 :(得分:2)

你有:

desired_thing = ids.detect do |id|
  thing = Thing.new(id)
  return thing if thing.true?
end

如果找到thing thing.true?true的{​​{1}},则detect永远不会返回ids的元素(将被分配到desired_thing )因为它被return抢占了。另一方面,如果块在未调用return的情况下完成,detect会返回nil并将该值分配给desired_thing,但nil值为ids.each do |id| thing = Thing.new(id) return thing if thing.true? end 在后面的代码中没有用。因此,最好只写:

{{1}}

答案 1 :(得分:1)

我想你差点就到了。

find_by_id方法,我假设Question是一个具有某种ActiveRecord功能的类,所以我希望也存在where方法。

在那种情况下:

applicable_question = Question.where(id: ids).order(...).detect do |question|
  question.applicable_for?(self)
end

会做得很好。

它不想滥用detect,你只需拥有正确的可枚举对象集。

更新

如果实例化太多对象不是一个可接受的解决方案,那么https://stackoverflow.com/a/29176622/687142就是可行的方法。请记住,这两种方法都有其缺点。

  • Question.where(id: ids).order(...).detect {|q| q.condition? }将对数据库执行单个查询,但会实例化太多对象。
  • ids.each {|id| q = Question.find(id); return q if q.condition? }将对数据库执行太多查询,但一次只实例化一个对象

第一种方法是持续繁重(记忆明智),而第二种方法的性能取决于您检索记录的顺序,并且也可能变得非常昂贵。

最佳选择也取决于您的数据集。如果您有数十万个问题,那么第一种方法是不可能的。

也许最好的选择是尝试以condition?更可能真实的方式订购记录

答案 2 :(得分:0)

你所拥有的与你想要做的事情相去甚远。我会做类似的事情:

desired_thing = ids.detect{ |id| Thing.new(id) }

我不确定你是否试图远离像find_by_id这样的SQL方法,或者在哪里,但我相信你也可以使用它们,除非你需要使用detect(或find)。