Удалить запись, если объект существует только в одном из двух массивов - PullRequest
0 голосов
/ 07 мая 2020

У меня есть два массива, которые представляют записи из таблицы User.

@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

Какой метод является оптимальным для этой проблемы?

Ответы [ 3 ]

2 голосов
/ 07 мая 2020

Я могу придумать два разных способа сделать это.

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

Я бы предпочел первый вариант, поскольку второй запрос может закончится отправкой большого запроса к базе данных, а также тестирование первого решения будет намного проще с помощью простого модульного тестирования.

2 голосов
/ 07 мая 2020

Вы, по сути, просите найти все общие элементы, и это очень легко сделать с помощью Array#&:

a = %w[ john sarah george ]
b = %w[ sarah john ringo ]

a & b
# => ["john", "sarah"]

Это находит объединение двух наборов, или, другими словами, удаляет записи, отсутствующие в обоих.

Чтобы найти те, которые нужно удалить, вы также можете использовать Array#-, чтобы вычесть их:

to_delete = a - b
1 голос
/ 07 мая 2020

Я бы сначала сопоставил обе коллекции с их идентификаторами:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...