Elixir - 将字符串数字或空字符串转换为浮点数或零
Elixir - Convert a string number or empty string to a float or nil
我正在尝试将 price
字段(例如 "2.22"
或 ""
)转换为浮点数或 nil,然后将其添加到数据库中。
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 (,,,) RETURNING "id" [0, 0, 40, 1]
如输出所示,priceFloat
变为零,我假设是因为当我将它设置为 2.22 时它超出了范围。也许我的代码太命令了。我如何重写它以将“2.22”转换为 2.22 而不使其为 nil,并允许将“”转换为 nil?
您可以结合使用模式匹配和方法重载来解决问题:
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
使用内置调试器。
您可以使用 case
来评估 Float.parse
的返回值,并在 returns :error
时分配 nil
,假设您的目的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
As the output shows, priceFloat becomes nil, I assume because when I set it to 2.22 it was out of scope.
差不多吧。而不是您尝试设置的变量超出范围,问题是您分配给 inside if 语句的变量 out of范围。它恰好与 if 语句外的变量同名。
解决方法是将if/else语句的结果赋值给变量。这是您的代码,改动很少:
price = "2.22"
priceFloat =
if price not in [""] do
elem(Float.parse(price), 0)
else
nil
end
IO.inspect(priceFloat)
但是,它仍然不是很地道。您可以利用 Float.parse/1
returns :error
当输入为空字符串时像使用 case
表达式一样编写它:
priceFloat =
case Float.parse(price) do
{float, ""} -> float
:error -> nil
end
为了多样性,我会 post 另一种使用 with/1
特殊形式的方法。
with {f, ""} <- Float.parse("3.14"),
do: f,
else: (_ -> nil)
这里我们只显式匹配浮点数。任何拖尾的垃圾都将被丢弃。如果匹配成功,我们 return 浮点数,否则,我们 return nil
.
小心Float.parse/1
可能会被看起来像科学记数法的垃圾弄糊涂。
(with {f, ""} <- Float.parse("3e14"), do: f) == 300_000_000_000_000
#⇒ true
重要旁注:在if
中分配priceFloat
不会改变[=16的值=] 范围外的变量。 elixir 中的作用域非常重要,与大多数语言不同,不能将局部变量传播到最外层的作用域。
foo = 42
if true, do: foo = 3.14
IO.puts(foo)
#⇒ 42
好吧,在某种程度上,可以使用 var!/2
影响宏的最外层范围变量,而 if
确实是一个宏,但是这一切肯定远远超出了这个问题的范围.
我正在尝试将 price
字段(例如 "2.22"
或 ""
)转换为浮点数或 nil,然后将其添加到数据库中。
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 (,,,) RETURNING "id" [0, 0, 40, 1]
如输出所示,priceFloat
变为零,我假设是因为当我将它设置为 2.22 时它超出了范围。也许我的代码太命令了。我如何重写它以将“2.22”转换为 2.22 而不使其为 nil,并允许将“”转换为 nil?
您可以结合使用模式匹配和方法重载来解决问题:
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
使用内置调试器。
您可以使用 case
来评估 Float.parse
的返回值,并在 returns :error
时分配 nil
,假设您的目的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
As the output shows, priceFloat becomes nil, I assume because when I set it to 2.22 it was out of scope.
差不多吧。而不是您尝试设置的变量超出范围,问题是您分配给 inside if 语句的变量 out of范围。它恰好与 if 语句外的变量同名。
解决方法是将if/else语句的结果赋值给变量。这是您的代码,改动很少:
price = "2.22"
priceFloat =
if price not in [""] do
elem(Float.parse(price), 0)
else
nil
end
IO.inspect(priceFloat)
但是,它仍然不是很地道。您可以利用 Float.parse/1
returns :error
当输入为空字符串时像使用 case
表达式一样编写它:
priceFloat =
case Float.parse(price) do
{float, ""} -> float
:error -> nil
end
为了多样性,我会 post 另一种使用 with/1
特殊形式的方法。
with {f, ""} <- Float.parse("3.14"),
do: f,
else: (_ -> nil)
这里我们只显式匹配浮点数。任何拖尾的垃圾都将被丢弃。如果匹配成功,我们 return 浮点数,否则,我们 return nil
.
小心Float.parse/1
可能会被看起来像科学记数法的垃圾弄糊涂。
(with {f, ""} <- Float.parse("3e14"), do: f) == 300_000_000_000_000
#⇒ true
重要旁注:在if
中分配priceFloat
不会改变[=16的值=] 范围外的变量。 elixir 中的作用域非常重要,与大多数语言不同,不能将局部变量传播到最外层的作用域。
foo = 42
if true, do: foo = 3.14
IO.puts(foo)
#⇒ 42
好吧,在某种程度上,可以使用 var!/2
影响宏的最外层范围变量,而 if
确实是一个宏,但是这一切肯定远远超出了这个问题的范围.