【问题标题】:Elixir 'if' and 'and' not working as expectedElixir 'if' 和 'and' 没有按预期工作
【发布时间】:2017-05-26 15:06:58
【问题描述】:

我有这个代码:

if Map.has_key?(dbShop, "id") && Map.has_key?(dbProduct, "id") do
  case Api.Repo.insertProductShop(conn, dbShop.id, dbProduct.id) do
    {:ok, productShop} ->
      {:ok, productShop}
    {:error, changeset} ->
      Tuple.append(errors, "could not insert product in the Shop")
  end
else
  IO.puts("(dbShop, id) && Map.has_key?(dbProduct, id) failed")
  IO.inspect(dbShop)
  IO.inspect(dbProduct)
end

代码执行进入else 子句并将其记录到控制台:

(dbShop, id) && Map.has_key?(dbProduct, id) failed
%Api.Shop{__meta__: #Ecto.Schema.Metadata<:loaded, "shops">, id: 23,
 latitude: -36.846691, longitude: 174.7745803, name: "Yard Bar & Eatery",
 placeId: "ChIJp6DGbAdIDW0RcbnExyPHvCk"}
%Api.Product{__meta__: #Ecto.Schema.Metadata<:loaded, "products">,
 brand: "baba", description: " zhzngshshhshs", id: 34, image: "no-image",
 name: "Nsn", numberOfVotes: nil, rating: nil}

所以我们可以看到dbShopdbProduct 都有一个id,并且不知何故,代码执行永远不会进入if 子句。我的if 子句做错了什么?我想检查它们是否都有id,如果有,请进入if 子句。

路由器中的完整功能:

  post "/products" do
    errors = {}
    postedProduct = conn.body_params
    dbProduct = %{}
    dbShop = %{}
    case Api.Repo.insertProduct(conn, postedProduct) do
      {:success, product} ->
        dbProduct = product
        case Api.Repo.insertProductCategories(conn, postedProduct, dbProduct.id) do
          {:ok, categories} ->
            {:ok, categories}
          {:error, failed_operation, failed_value, changes_so_far} ->
            Tuple.append(errors, "could not insert productCategories. Product already has that category")
        end
      {:error, changeset} ->
        IO.puts("product not inserted")
        Tuple.append(errors, "could not insert product. Product already existed")
        IO.inspect(errors)
    end

    if Map.has_key?(postedProduct, "shop") do
      case Api.Repo.insertShop(conn, postedProduct["shop"]) do
        {:ok, shop} ->
          dbShop = shop
          if Map.has_key?(dbShop, :id) && Map.has_key?(dbProduct, :id) do
            case Api.Repo.insertProductShop(conn, dbShop.id, dbProduct.id) do
              {:ok, productShop} ->
                {:ok, productShop}
              {:error, changeset} ->
                Tuple.append(errors, "could not insert product in the Shop")
            end
          else
            IO.puts("(dbShop, id) && Map.has_key?(dbProduct, id) failed")
            IO.inspect(dbShop)
            IO.inspect(dbProduct)
          end
        {:error, changeset} -> # shop already exists
          # Tuple.append(errors, "could not insert shop")
          if Map.has_key?(dbShop, "id") && Map.has_key?(dbProduct, "id") do
            case Api.Repo.insertProductShop(conn, dbShop.id, dbProduct.id) do
              {:ok, productShop} ->
                {:ok, productShop}
              {:error, changeset} ->
                Tuple.append(errors, "The product has already been added to the shop")
            end
          end
      end
    end

    if tuple_size(errors) > 0 do
      IO.puts("errors")
      IO.inspect(errors)
      conn
        |> put_resp_content_type("application/json")
        |> send_resp(200, Poison.encode!(%{
            successs: "success",
            errors: errors
        }))
    else 
      conn
        |> put_resp_content_type("application/json")
        |> send_resp(200, Poison.encode!(%{
            successs: "success"
        }))
    end
  end

【问题讨论】:

  • 你的钥匙是原子。试试Map.has_key?(..., :id)
  • 另外,你想用这个表达式做什么?如果这两个值始终是 Shop 和 Product 结构,它们将始终“拥有”键 :id,即使 :id 的值为 nil 或 false。
  • @Dogbert 谢谢!我想因为它在id 前面没有:,所以它不是一个原子。关于您的第二条评论,dbProduct 只有在产品插入数据库时​​才会被填充。如果它没有被填充,它很可能已经在数据库中(唯一约束)。
  • 你所说的“得到填充”是什么意思?如果dbProduct 不是结构,则会抛出异常,如果它是结构,则始终具有:id 字段。
  • @Dogbert 我刚刚将完整功能添加到问题中,以便您查看。它需要一个大的重构。

标签: ecto elixir


【解决方案1】:

您的地图有一个原子:id 作为键,而不是字符串"id",所以您的if 应该是:

if Map.has_key?(dbShop, :id) && Map.has_key?(dbProduct, :id) do

如果该值保证为非零(如果存在),您还可以将其缩短为:

if dbShop[:id] && dbProduct[:id] do

如果键不存在,使用方括号语法访问值不会引发错误,而是返回 nil,这是 Elixir 中的假值。

【讨论】:

    【解决方案2】:

    在 Elixir 中使用 if 是一种代码异味,并暗示代码可能会以更明确的方式重写。在这里我会选择Kernel.SpecialForms.with/1,而不是:

    if Map.has_key?(dbShop, :id) && Map.has_key?(dbProduct, :id) do
      # true
    else
      # false
    end
    

    使用:

    with %{id: shopId} when not is_nil(shopId) <- dbShop,
         %{id: prodId} when not is_nil(prodId) <- dbProduct do
      # true
    else
      ^dbProduct -> IO.puts "prodId is nil"
      ^dbShop -> IO.puts "shopId is nil"
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-26
      • 1970-01-01
      • 2017-06-18
      • 1970-01-01
      相关资源
      最近更新 更多