【问题标题】:Elixir - Convert a string number or empty string to a float or nilElixir - 将字符串数字或空字符串转换为浮点数或零
【发布时间】:2020-04-05 16:12:51
【问题描述】:

我正在尝试将price 字段,它是一个字符串(例如"2.22""")转换为float 或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 ($1,$2,$3,$4) RETURNING "id" [0, 0, 40, 1]

如输出所示,priceFloat 变为 nil,我认为是因为当我将其设置为 2.22 时,它超出了范围。也许我的代码太迫切了。如何重写它以将“2.22”转换为 2.22 而不会使其为零,并允许将“”转换为 nil?

【问题讨论】:

    标签: ecto elixir


    【解决方案1】:

    正如输出所示,priceFloat 变为 nil,我认为是因为当我将其设置为 2.22 时,它超出了范围。

    几乎是对的。而不是您尝试设置的变量超出范围,问题是您分配给 inside if 语句的变量超出了范围。它恰好与 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 在输入为空字符串时返回:error 的事实来编写它,就像使用case 表达式一样:

    priceFloat =
      case Float.parse(price) do
        {float, ""} -> float
        :error -> nil
      end
    

    【讨论】:

    • 盲目的elem/2 具有潜在的危险性,因为它很乐意与Float.parse("3.14some❤❤❤garbage") 打交道。
    • 这就是我建议匹配{float, ""}的原因。
    • 我知道 :) 我只是指出你最好提一下(这不仅违反习惯,而且有些危险。)
    • 好点,我应该在答案中明确解释,但现在你为我做了;-)
    【解决方案2】:

    您可以使用case 来评估Float.parse 的返回值,并在返回: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
    

    【讨论】:

    • 这段代码有潜在的危险,因为它很乐意接受Float.parse("3.14some❤❤❤garbage")
    • @AlekseiMatiushkin 真的吗?但是垃圾会留在剩余部分,不是吗?怎么会有危险?顺便说一句,我很欣赏评论,学习这些东西总是很好
    • 垃圾确实会留在剩余部分,但是1.0a被成功解析为float,而a1.0返回错误似乎不是超级一致的:)
    【解决方案3】:

    您可以结合使用模式匹配和方法重载来解决问题:

    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 使用内置调试器。

    【讨论】:

    • 谢谢!当我传入 "": (MatchError) no match of right hand side value: :error 时出现错误。我想在price""的情况下返回nil
    • {float, _} 模式匹配存在潜在危险,因为它很乐意匹配 Float.parse("3.14some❤❤❤garbage")
    • 另外,def parsePrice(""), do: nil 子句是多余的,空字符串会被 Float.parse/1 自己排除。
    【解决方案4】:

    为了多样化,我会发布另一种使用with/1 特殊形式的方法。

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

    这里我们只显式匹配浮点数。任何尾随垃圾都将被丢弃。如果匹配成功,则返回浮点数,否则返回nil


    注意Float.parse/1 可能会被看起来像科学记数法的垃圾所迷惑。

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

    重要的旁注:在if 内分配priceFloat 不会更改范围外的priceFloat 变量的值。 中的作用域非常重要,与大多数语言不同,不能将局部变量传播到最外层作用域。

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

    嗯,在某种程度上,var!/2 可以影响宏的最外层范围变量,而if 确实是一个宏,但这整个东西肯定远远超出了这个问题的范围。

    【讨论】:

      猜你喜欢
      • 2014-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-25
      • 1970-01-01
      • 2012-05-04
      • 2019-10-03
      • 1970-01-01
      相关资源
      最近更新 更多