如何使用Enum而不是If语句有条件地减少变更集

时间:2019-02-08 08:18:50

标签: elixir

我正在使用一堆可选字段对变更集进行加密,但是我目前正在使用大量的If语句来查看变更集是否包含字段,然后再尝试对其进行加密。

我感觉到有一个Enum函数(如reduce)可以以一种比普通Elixir更好的方式实现此目的,但是我想出的所有方法都没有比一堆丑陋的If语句更有效。 / p>

 def encrypt(changeset) do
    if changeset.changes["address"] do
      {:ok, encrypted_address} = EncryptedField.dump(changeset.changes.address, key_id)
      changeset
      |> put_change(:address, encrypted_address)
    end
    if changeset.changes["dob"] do
      {:ok, encrypted_dob} = EncryptedField.dump(changeset.changes.dob, key_id)
      changeset
      |> put_change(:address, encrypted_dob)
    end
    if changeset.changes["email"] do
      {:ok, encrypted_email} = EncryptedField.dump(changeset.changes.email, key_id)
      changeset
      |> put_change(:email, encrypted_email)
    end
   ...
 end

2 个答案:

答案 0 :(得分:4)

您需要做的是遍历字段,有条件地更新变更集:

def encrypt(changeset) do
  Enum.reduce(~w[address dob email]a, changeset, fn field, changeset ->
    if changeset.changes[field] do
      {:ok, encrypted} =
        EncryptedField.dump(changeset.changes[field], key_id)
      put_change(changeset, field, encrypted)
    else
      changeset # unlike your implementation this works
    end
  end)
end

另一种方法是首先Enum.filter/2个字段:

def encrypt(changeset) do
  ~w[address dob email]a
  |> Enum.filter(&changeset.changes[&1])
  |> Enum.reduce(changeset, fn field, changeset ->
    {:ok, encrypted} =
      EncryptedField.dump(changeset.changes[field], key_id)
    put_change(changeset, field, encrypted)
  end)
end

侧注:根据核心团队的样式准则,如果且链中只有很多链接,则应使用管道。


回答第二个答案:

使用with的惯用代码为:

def encrypt(changeset) do
  ~w[address dob email] |> Enum.reduce(changeset, fn field ->
    with %{^field => value} <- changeset.changes,
          {:ok, encrypted} <- EncryptedField.dump(value, key_id)
    do
      put_change(changeset, field, encrypted)
    else
      _ -> changeset
    end
  end)
end

答案 1 :(得分:1)

同意Aleksei的回答。

长生不老药的另一种模式是,如果在运行表达式之前进行了一系列检查,则可以使用with

使用with的Aleksei的代码类似于

def encrypt(changeset) do
  ~w[address dob email] |> Enum.reduce(changeset, fn field ->
    with \
      true <- Map.has_key?(changeset.changes, field),
      {:ok, encrypted} <- EncryptedField.dump(changeset.changes[field], key_id)
    do
      put_change(changeset, field, encrypted)
    else
      _ -> changeset
    end
  end)
end

在这里,如果EncryptedField.dump返回一个:error,则返回{changeset}而不是抛出{:ok, val} = EncryptedField.dump(...)时引发异常。

P.S:还建议使用Map.has_key?