如果对象仅存在于两个数组之一中,则删除记录
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
请注意,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
我可以想到两种不同的方法。
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
我宁愿选择第一个选项,因为第二个查询可能最终会向数据库发送一个大查询,而且仅通过单元测试测试第一个解决方案会简单得多。
我有两个数组表示来自用户 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
请注意,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
我可以想到两种不同的方法。
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
我宁愿选择第一个选项,因为第二个查询可能最终会向数据库发送一个大查询,而且仅通过单元测试测试第一个解决方案会简单得多。