Delete the 1st pair in a map

Can you show us the final code snippet? Sharing the solution could help someone else in the future.

Yep
The task is to find duplicate entries and delete everything and spare the most optimal one

defmodule Mix.Tasks.App.CleanUpDuplicateAccounts do
  use Mix.Task

  import Ecto.Query, only: [where: 3, from: 2]

  alias App.Data.{....
  }

  require Logger

  def run(_args) do
    Logger.info("Starting task to clean up duplicate accounts...")

    {:ok, _} = Application.ensure_all_started(:data)

    query =
      from(u in User,
        where:
          fragment(
            "exists (
                select 1
                from users inner
                where inner.id != ?
                and lower(inner.email) = lower(?)
              )",
            u.id,
            u.email
          ),
        order_by: u.email
      )

    query
    |> Repo.all()
    |> Enum.group_by(&String.downcase(&1.email))
    |> Enum.each(fn {_k, duplicates} ->
      comparing_statuses(duplicates)
    end)
  end

  defp get_tasks_for_users(id) do
        # Logic return tasks
  end

  defp check_and_remove_account(user) do
    if !user.is_email_verified ||
         is_nil(user.encrypted_password) do
      tasks = get_tasks_for_user(user.id)

      case tasks do
        [] ->
          Logger.info("Deleting user: id #{user.id} with email #{user.email}")
          Users.delete(user)

        _ ->
          Logger.info("Unable to delete - user: id #{user.id} has active tasks")
      end
    end
  end

  defp mass_deletion_check(duplicates) do
    are_all_duplicates_not_verified =
      Enum.all?(duplicates, &(!&1.is_email_verified || is_nil(&1.encrypted_password)))

    if are_all_duplicates_not_verified do
      [_ | duplicates_to_delete] = duplicates

      Enum.each(
        duplicates_to_delete,
        fn index ->
          check_and_remove_account(index)
        end
      )
    else
      Enum.each(duplicates, fn index ->
        if !index.is_email_verified or is_nil(index.encrypted_password) do
          check_and_remove_account(index)
        else
          Logger.info(
            "Not deleting - user: id #{index.id} has email is verified or password is set"
          )
        end
      end)
    end
  end

  defp get_handle_for_user(id) do
        # logic to return handles
  end

  # If handles are the same then start mass_deletion_check for the duplicates
  # If handles are different then delete email from accounts except one
  defp handle_or_email_deletion_check(duplicates) do
    duplicates_with_handle =
      duplicates
      |> Enum.reduce([], fn user, acc ->
        acc ++ [Map.put(user, :handle, get_handle_for_user(user.id))]
      end)

    handle_group = Enum.group_by(duplicates_with_handle, & &1.handle)

    if Enum.count(handle_group) < 2 do
      mass_deletion_check(duplicates)
    else
      [_ | handle_group_to_delete_email] = Enum.to_list(handle_group)

      Enum.each(handle_group_to_delete_email, fn {_k, duplicates} ->
        remove_email_except_one(duplicates)
      end)
    end
  end

  defp remove_email_except_one(duplicates) do
    Enum.each(duplicates, fn user ->
      Users.update(user, %{email: nil})
    end)
  end

  defp count_with_status(duplicates, status) when not is_list(status) do
    count_with_status(duplicates, [status])
  end

  defp count_with_status(duplicates, status) do
    Enum.count(
      duplicates,
      &(&1.user_status_id in UserStatuses.get_id_by_code(status))
    )
  end

  defp accumulating(duplicates, status) do
    duplicates
    |> Enum.reduce([], fn user, acc ->
      if user.user_status_id in UserStatuses.get_id_by_code(status) do
        acc ++ [user]
      else
        check_and_remove_account(user)
        acc
      end
    end)
  end

  defp accumulating_accounts_with_required_status(duplicates, status)
       when status == "approval_requested" do
    duplicates_account_with_approval_requested = accumulating(duplicates, [status])

    delete_all_and_update_first_one(duplicates_account_with_approval_requested)
  end

  defp accumulating_accounts_with_required_status(duplicates, status) when not is_list(status) do
    duplicates_to_delete = accumulating(duplicates, [status])

    mass_deletion_check(duplicates_to_delete)
  end

  defp accumulating_accounts_with_required_status(duplicates, status) do
    duplicates_to_delete = accumulating(duplicates, status)

    mass_deletion_check(duplicates_to_delete)
  end

  defp iterate_and_remove(duplicates, status)
       when not is_list(status) do
    iterate_and_remove(duplicates, [status])
  end

  defp iterate_and_remove(duplicates, status) do
    Enum.each(duplicates, fn user ->
      if user.user_status_id not in UserStatuses.get_id_by_code(status) do
        check_and_remove_account(user)
      end
    end)
  end

  defp iterate_and_update(duplicates, status) do
    Enum.each(duplicates, fn user ->
      if user.user_status_id ==
           UserStatuses.get_id_by_code(status) do
        Users.update(user, %{email: nil})
      end
    end)
  end

  defp iterate_and_update(duplicates, status_one, status_two) do
    Enum.each(duplicates, fn user ->
      if user.user_status_id !==
           UserStatuses.get_id_by_code(status_one) do
        check_and_remove_account(user)
      else
        Users.update(user, %{
          user_status_id: UserStatuses.get_id_by_code(status_two)
        })
      end
    end)
  end

  defp deleting_with_approached_and_approved_case(duplicates) do
    duplicates_approved_and_approached =
      Enum.reduce(duplicates, [], fn user, acc ->
        if user.user_status_id !== UserStatuses.get_id_by_code("approved") and
             user.user_status_id !==
               UserStatuses.get_id_by_code("approached") do
          check_and_remove_account(user)
          acc
        else
          acc ++ [user]
        end
      end)

    handle_from_approved =
      get_handle_for_user(Enum.at(duplicates_approved_and_approached, 0).id)

    handle_from_approached =
      get_handle_for_user(Enum.at(duplicates_approved_and_approached, 1).id)

    case {handle_from_approved, handle_from_approached} do
      {handle_from_approved, handle_from_approached}
      when handle_from_approved == handle_from_approached and not is_nil(handle_from_approved) and
             not is_nil(handle_from_approached) ->
        iterate_and_remove(
          duplicates_approved_and_approached,
          "approached"
        )

      _ ->
        iterate_and_update(
          duplicates_approved_and_approached,
          "approached"
        )
    end
  end

  defp delete_all_and_update_first_one(duplicates) do
    [user_to_update | duplicates_to_delete] = duplicates
    Enum.each(duplicates_to_delete, fn user -> check_and_remove_account(user) end)

    Users.update(user_to_update, %{
      user_status_id: UserStatuses.get_id_by_code("approved")
    })
  end

  defp count_duplicates_statuses(duplicates) do
    approved_count = count_with_status(duplicates, "approved")

    invalid_count =
      count_with_status(duplicates, [
        "invalid",
        "under_investigation",
        "suspended"
      ])

    approached_count = count_with_status(duplicates, "approached")

    rejected_count = count_with_status(duplicates, "rejected")

    external_count = count_with_status(duplicates, "external")

    approval_requested_count = count_with_status(duplicates, "approval_requested")

    {approved_count, invalid_count, approached_count, rejected_count, external_count,
     approval_requested_count}
  end

  defp comparing_statuses(duplicates) do
    statuses = count_duplicates_statuses(duplicates)
    handle_statuses(statuses, duplicates)
  end

  defp handle_statuses({1, _, 0, _, _, _}, duplicates) do
    iterate_and_remove(duplicates, "approved")
  end

  defp handle_statuses({1, _, 1, _, _, _}, duplicates) do
    deleting_with_approached_and_approved_case(duplicates)
  end

  defp handle_statuses(
         {approved_count, _, 0, _, _, _},
         duplicates
       )
       when approved_count >= 2 do
    accumulating_accounts_with_required_status(duplicates, "approved")
  end

  defp handle_statuses({0, 1, _, _, _, _}, duplicates) do
    iterate_and_remove(duplicates, [
      "invalid",
      "under_investigation",
      "suspended"
    ])
  end

  defp handle_statuses({0, invalid_count, _, _, _, _}, duplicates) when invalid_count >= 2 do
    accumulating_accounts_with_required_status(duplicates, [
      "invalid",
      "under_investigation",
      "suspended"
    ])
  end

  defp handle_statuses({0, 0, 1, _, _, 0}, duplicates) do
    iterate_and_remove(duplicates, "approached")
  end

  defp handle_statuses({0, 0, 1, _, _, 1}, duplicates) do
    iterate_and_update(
      duplicates,
      "approval_requested",
      "approved"
    )
  end

  defp handle_statuses({0, 0, approached_count, _, _, approval_requested_count}, duplicates)
       when approached_count >= 2 and approval_requested_count >= 2 do
    accumulating_accounts_with_required_status(duplicates, "approval_requested")
  end

  defp handle_statuses({0, 0, approached_count, _, _, 0}, duplicates)
       when approached_count >= 2 do
    iterate_and_remove(duplicates, "approached")

    handle_or_email_deletion_check(duplicates)
  end

  defp handle_statuses({0, 0, 0, 1, _, _}, duplicates) do
    iterate_and_remove(duplicates, "rejected")
  end

  defp handle_statuses({0, 0, 0, rejected_count, _, _}, duplicates) when rejected_count >= 2 do
    accumulating_accounts_with_required_status(duplicates, "rejected")
  end

  defp handle_statuses({0, 0, 0, 0, 1, _}, duplicates) do
    iterate_and_remove(duplicates, "external")
  end

  defp handle_statuses({0, 0, 0, 0, external_count, _}, duplicates) when external_count >= 2 do
    accumulating_accounts_with_required_status(duplicates, "external")
  end

  defp handle_statuses({0, 0, 0, 0, 0, approval_requested_count}, duplicates)
       when approval_requested_count >= 2 do
    mass_deletion_check(duplicates)
  end
end
2 Likes