我有两个数组来表示用户表中的记录。
@server = [{ id: 1, name: "john" }, { id: 2, name: "Sarah" }, { id: 3, name: "George" }]
@client = [{ id: 1, name: "john" }, { id: 2, name: "Sarah" }]
我想运行一个函数来检查一个数组与另一个数组并删除 George 的记录,因为它们不再存在于@client
目前,我有这种相当长的方法,它有效但绝对不是最佳的。
@server.each do |item|
if @client.select{ |obj| obj[:id] == item.id }.length < 1
User.find(item.id).delete
end
end
解决此问题的最佳方法是什么?
你本质上是要求找到所有常见元素,这很容易用Array#&
做到:
a = %w[ john sarah george ]
b = %w[ sarah john ringo ]
a & b
# => ["john", "sarah"]
这将找到两个集合的并集,或者换句话说,删除两个集合中不存在的条目。
要找出需要删除的那些,您还可以使用Array#-
将它们减去:
to_delete = a - b
我可以想到两种不同的方法来做到这一点。
1)使用Array#-方法获取差异,并像这样直接从数据库中删除这些条目;
ids_to_delete = (server - client).map { |entry| entry[:id] }
User.where(id: ids_to_delete).delete_all
2)第二种方法是让数据库引擎为您完成这项工作;
server_ids = server.map { |entry| entry[:id] }
client_ids = client.map { |entry| entry[:id] }
User.where(id: server_ids).where.not(id: client_ids).delete_all
我宁愿选择第一个选项,因为第二个查询最终可能会向数据库发送一个大查询,并且仅通过单元测试测试第一个解决方案会简单得多。
我将首先将两个集合映射到它们的 id:
server_user_ids = @server.map { |user| user[:id] }
client_user_ids = @client.map { |user| user[:id] }
然后,您可以使用以下方法删除所有用户:
User.where(id: server_user_ids - client_user_ids).delete_all
请注意,delete
和delete_all
都不会触发回调。如果要触发回调,请改用destroy
或destroy_all
。
如果@server
和@client
可以容纳大型集合,我建议改用集合。它们的查找时间更快,但可读性会降低一点。
server_user_ids = @server.map { |user| user[:id] }.to_set
client_user_ids = @client.map { |user| user[:id] }.to_set
User.where(id: server_user_ids - client_user_ids).delete_all
我不确定where
是否适用于集合。如果没有,请将上述更改为[*server_user_ids - client_user_ids]
,这会将结果集分散到数组中。还可以对生成的集调用to_a
。(server_user_ids - client_user_ids).to_a