Elixir - конвертирует номер строки или пустую строку в число с плавающей точкой или ноль - PullRequest
0 голосов
/ 05 апреля 2020

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

  def insert_product_shop(conn, product_id, shop_id, price) do
    priceFloat = nil
    if price not in [""] do
      price = elem(Float.parse(price), 0)
      priceFloat = price / 1
      IO.inspect(priceFloat)
    else
      priceFloat = nil
    end
    IO.inspect(priceFloat)
    changeset = Api.ProductShop.changeset(%Api.ProductShop{
      p_id: product_id,
      s_id: shop_id,
      price: priceFloat,
      not_in_shop_count: 0,
      is_in_shop_count: 0
      })
    errors = changeset.errors
    valid = changeset.valid?
    IO.inspect(changeset)
    case insert(changeset) do
      {:ok, product_shop} ->
        {:ok, product_shop}
      {:error, changeset} ->
        {:error, :failure}
    end
  end

вывод:

2.22
nil
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #Api.ProductShop<>,
 valid?: true>

13:25:41.745 [debug] QUERY OK db=2.0ms
INSERT INTO "product_shops" ("is_in_shop_count","not_in_shop_count","p_id","s_id") VALUES ($1,$2,$3,$4) RETURNING "id" [0, 0, 40, 1]

Как показывает вывод, priceFloat становится равным нулю, я предполагаю, потому что, когда я установил его на 2,22, он был вне области видимости. Может быть, мой код слишком обязателен. Как я могу переписать это, чтобы преобразовать "2.22" в 2.22, не делая его ноль, и разрешить "" преобразовать в ноль?

Ответы [ 4 ]

2 голосов
/ 05 апреля 2020

Как показывает вывод, priceFloat становится равным нулю, я предполагаю, потому что, когда я установил его на 2,22, он был вне области видимости.

Почти верно. Скорее всего, из-за того, что переменная, которую вы пытаетесь установить, находится вне области видимости, проблема заключается в том, что переменная, которую вы присваиваете внутри оператора if, выходит из области видимости. У него просто имя совпадает с именем переменной вне оператора if.

Решение состоит в том, чтобы присвоить результат оператора if / else переменной. Вот ваш код с минимальными изменениями:

price = "2.22"

priceFloat =
  if price not in [""] do
    elem(Float.parse(price), 0)
  else
    nil
  end

IO.inspect(priceFloat)

Однако, он все еще не очень идиоматический c. Вы можете воспользоваться тем фактом, что Float.parse/1 возвращает :error, когда на входе есть пустая строка, чтобы написать ее, как выражение case:

priceFloat =
  case Float.parse(price) do
    {float, ""} -> float
    :error -> nil
  end
1 голос
/ 05 апреля 2020

Ради разнообразия я бы опубликовал другой подход, использующий специальную форму with/1.

with {f, ""} <- Float.parse("3.14"),
  do: f,
  else: (_ -> nil)

Здесь мы явно сопоставляем только число с плавающей точкой. Любой завершающий мусор будет отброшен. Если совпадение выполнено успешно, мы возвращаем число с плавающей точкой, в противном случае мы возвращаем nil.


Остерегайтесь Float.parse/1, может быть сбит с толку мусором, который выглядит как научный c Обозначения.

(with {f, ""} <- Float.parse("3e14"), do: f) == 300_000_000_000_000
#⇒ true

Важные sidenote : назначение priceFloat внутри if не изменяет значение переменной priceFloat вне сфера Область видимости в очень важна, и нельзя распространять локальные переменные во внешнюю область, в отличие от большинства языков.

foo = 42
if true, do: foo = 3.14
IO.puts(foo)
#⇒ 42

Ну, в некоторой степени возможно воздействовать на внешние переменные области видимости из макросов с помощью var!/2, и if действительно является макросом, но весь этот материал определенно далек выходит за рамки этого вопроса.

1 голос
/ 05 апреля 2020

Вы можете использовать case, чтобы оценить возвращаемое значение по Float.parse и назначить nil, когда оно вернет :error, предполагая, что цель вашего if состоит в том, чтобы избежать ошибки синтаксического анализа

def insert_product_shop(conn, product_id, shop_id, price) do
  priceFloat = case Float.parse(price) do
    {value, _remainder} -> value
    :error -> nil
  end
  ...
end
1 голос
/ 05 апреля 2020

Вы можете использовать комбинацию сопоставления с образцом и перегрузки методов для решения проблемы:

defmodule Example do
  def parsePrice(""), do: nil
  def parsePrice(price) when is_float(price), do: price
  def parsePrice(price) when is_binary(price) do
    {float, _} = Float.parse(price)
    float
  end
end

Example.parsePrice(2.22) |> IO.inspect
Example.parsePrice("2.22") |> IO.inspect

(эквивалент достигается с помощью оператора case)

Если вы передадите что-нибудь это не двоичный файл (строка) или число с плавающей точкой для этой функции, это приведет к ошибке несогласованного шаблона. Это может быть полезно, если у вас есть некоторые отчеты об ошибках, чтобы вы могли обнаружить непредвиденное использование вашего кода.

Для лучшего процесса отладки я рекомендую вам использовать встроенный отладчик через IEx.pry/0.

...