如果对象仅存在于两个数组之一中,则删除记录

Delete Record if Object only exists in One of Two arrays

我有两个数组表示来自用户 table 的记录。

@server = [{ id: 1, name: "john" }, { id: 2, name: "Sarah" }, { id: 3, name: "George" }]
@client = [{ id: 1, name: "john" }, { id: 2, name: "Sarah" }]

我想要 运行 一个函数来检查一个数组与另一个数组并删除乔治的记录,因为它们不再存在于 @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

我会先将这两个集合映射到它们的 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

请注意,deletedelete_all 都不会触发回调。如果您确实想触发回调,请改用 destroydestroy_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

我可以想到两种不同的方法。

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

我宁愿选择第一个选项,因为第二个查询可能最终会向数据库发送一个大查询,而且仅通过单元测试测试第一个解决方案会简单得多。