有效地迭代和变异关联

时间:2018-09-19 18:24:23

标签: ruby-on-rails collections ruby-on-rails-5

我们有有效执行此操作的代码。

obj.things.each |thing|
    ... do some stuff ...

    obj.things.destroy(thing)

    ... do some more stuff...
end

我们发现,在迭代CollectionProxy的同时更改CollectionProxy的结果是,只有一半的项目被迭代。目前,我们正在通过将代理展平为一个数组来解决此问题。但这意味着将所有things复制到内存中。

obj.things.to_a.each |thing|
    ...
end

是否有一种方法可以在不将整个关联放入内存的情况下对集合进行迭代和变异?

或者,是否有比我们正在使用的模式更好的模式?例如,包装代码是我们每次破坏关联时都不想做的事情,因此我们不使用关联钩子。我们可以编写一个可以使用钩子的子类或范围吗?

UPDATE I've posted about the larger issue

2 个答案:

答案 0 :(得分:1)

摧毁外观中的对象实际上是一个坏习惯。

我可以想到两种方法,您可以将要破坏的对象的id保存在数组上,或将它们标记在要删除的数据库上(添加一个默认为false的新布尔值列,然后执行 /** * This section makes posts in the admin filterable by the author. */ add_action('restrict_manage_posts', 'ditt_filter_by_author'); function ditt_filter_by_author() { $params = array( 'name' => 'author', 'role__in' => array('author','editor','administrator') ); if ( isset($_GET['user']) ) { $params['selected'] = $_GET['user']; } wp_dropdown_users( $params ); } )。

这样,您可以在循环之后执行Thing.where(id:ids_to_destroy).destroy_all(或者如果标记了Thing.where(to_destroy:true).destroy_all之类的东西)。

使用哪种方法取决于您的需求。我会把id保存在数组上,因为它需要较少的更改,但是也许您处理了大量的数据,并且在内存中保存非常大的数组实在太多了(不常见,因为您只是在存储id,但有可能)。

答案 1 :(得分:1)

您原来的each已经将整个关联拉入内存。

调用to_a产生额外的数组副本(然后没有突变)确实是一种非常合理的方法。而且并不是特别昂贵:数组是重复的,但实际的对象不是。

您也可以改用ActiveRecord::Base#destroy

obj.things.each |thing|
    ... do some stuff ...

    thing.destroy

    ... do some more stuff...
end

由于obj.things集合不再了解破坏,因此它将仍然包含全部内容,因此迭代将不受影响。 (不过,如果任何一个stuff块都在使用obj.things的当前内容,这显然是有问题的。)