【问题标题】:How to filter an ActiveRecord query result by comparison to another model instance?如何通过与另一个模型实例比较来过滤 ActiveRecord 查询结果?
【发布时间】:2016-04-29 13:51:13
【问题描述】:

我有一个简单的 ActiveRecord 查询:

similar_changes = Notification.where(change_owner: 'foo1', change_target: 'foo2', change_cancelled: false)

每个通知对象都有一个字段change_type,我还有另一个函数可以检查一个通知的change_type 和另一个通知的反向更改(在我的应用程序上下文中相互撤消的更改)。

我需要获取此通知的change_type 并将其与数组中的所有其他人进行比较。我必须像这样引用对象:similar_changes[0]['change_type'] 其中第一个索引是数组中的每个 ActiveRecord,第二个索引是指定 Notification 对象中的哪个属性的字典。

我觉得我可以使用两个嵌套循环和 if 语句手动执行此操作,但我也了解 Ruby,我觉得这是它应该内置的东西。

我错了,还是有更好的方法来做到这一点?

这是代码(请注意,所有这些代码都还没有完成,如果不完美,请耐心等待):

def self.group_similar_changes(owner, target, change_type)
  # long query where it selects all rows where change_owner and change_target 
  # are the same as original        
  # also where cancelled is false
  # determine if cancelled (yaml)
  # if cancelled (do nothing)
  similar_changes = Notification.where(
    change_owner: owner, 
    change_target: target, 
    change_cancelled: false
  )
  similar_changes.each do |change|
    cancel_inverse_change(change, change.change_type)
      if change.cancelled?
        similar_changes.delete(change)
      end
    end
  end
end

def cancel_inverse_change(change, change_type)
  if change.inverse?(change_type)
    change.cancel
  end
end

def inverse?(possible_inverse_change)
  is_inverse = false
  change_types = YAML.load_file(File.join(NotificationManager::Engine.root, 'config/change_types.yaml'))
  if self.change_type == change_types[possible_inverse_change]['inverse']
    is_inverse = true
  end
  return is_inverse
end

【问题讨论】:

  • 不,没有 Ruby 魔法可以做到这一点。使用循环。
  • “return inverse”中的“inverse”从何而来?还有 change_types 与 change_type
  • 啊,对不起。打错字了,抱歉。
  • 不需要赋值给is_inverse然后返回。只需返回 self.change == change_type[possible_inverse_change][1]['inverse'] (我假设 change_type 与 change_types 也是一个错字?)
  • 不相关,但您可能希望通过将除 YAML 加载之外的每一行替换为 return self.change == change_type[possible_inverse_change][1]['inverse'] 来清理此方法

标签: ruby-on-rails arrays ruby rails-activerecord


【解决方案1】:

是的,您对similar_changes 的循环可以改进。

  • 修改循环遍历的数组会让人感到困惑。我什至不知道它是否可靠,因为我从不这样做!
  • 依赖each 的返回值也不是惯用的Ruby。 each 通常用于对已经存在的 Enumerable 的元素做一些事情,所以使用它的返回值似乎很奇怪。

我会这样写

similar_changes.reject do |change|
  cancel_inverse_change(change, change.change)
  change.cancelled?
end

【讨论】:

  • 我假设如果它是 T/F,则拒绝方法会删除元素?
  • 是的,reject 删除块返回 true 的元素。
  • 太棒了,我会在我的代码中使用这些,如果我可以让它正常工作,我会选择答案。非常感谢您的反馈:)。
  • 我最终没有完全做到这一点,但它确实为我指明了正确的方向。我最终使用 Enum map 函数 + this 来进行循环和递归
猜你喜欢
  • 1970-01-01
  • 2020-11-12
  • 2021-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-16
  • 2020-06-02
  • 1970-01-01
相关资源
最近更新 更多