ИЛИ поведение в эликсире - PullRequest
0 голосов
/ 22 января 2020

У меня в следующей ситуации карта с логическим свойством false. Когда я попытался присвоить значение с условием or другой карте, я получил nil, а не false, как я ожидал.

iex(1)> my_map = %{"email_sended" => false}                          
%{"a" => false}

iex(2)> other_map = %{ email_sended: my_map["email_sended"] || my_map[:email_sended] }
%{email_sended: nil}

iex(3)> other_map[:email_sended]
nil

У меня такой вопрос, есть краткий способ сделать это без необходимости делать if условным, как это?

iex(4)> if Map.has_key?(my_map, "email_sended"), do: my_map["email_sended"], else: my_map[:email_sended]
false

Ответы [ 4 ]

1 голос
/ 22 января 2020

Если вы просто ищете короткое встроенное решение, просто ORing с ложным будет обрабатывать ноль:

iex> my_map = %{"email_sended" => false}
%{"email_sended" => false}
iex> my_map["email_sended"] || my_map[:email_sended] || false
false

Если вы ищете более надежные решения, case и with хорошие варианты:

iex> case my_map do
...>   %{"email_sended" => sent} -> sent
...>   %{email_sended: sent} -> sent
...>   %{} -> false
...> end
false
iex> with :error <- Map.fetch(my_map, "email_sended"),
...>      :error <- Map.fetch(my_map, :email_sended) do
...>   false
...> else
...>   {:ok, sent} ->
...>     sent
...> end
false
1 голос
/ 22 января 2020

(иногда считается грязным) трюк для преобразования любого значения в логическое значение - это double, а не :

iex> !!(nil || true)
true

iex> !!(nil || false)
false

Но имейте в виду, что любое значение, отличное от nil или false считается верным, поэтому не попадайтесь в эту ловушку:

iex> !!(true || "on") # seems to work
true

iex> !!(false || "off") # oops
true

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

1 голос
/ 22 января 2020

Если вы посмотрите на ||, && and ! документацию операторов, вы можете найти:

все значения, кроме false и nil, будут иметь значение true:

iex> false || 11
11

Это означает, что оператор || не может быть надежно использован для таких вещей.

В целом, ваш подход неверен, вы должны знать, с какими типами клавиш вы работаете, поскольку существуют преимущества / недостатки использования атомов / strings keys.

Если вы все еще хотите выполнить эту работу, наилучшим подходом было бы создание вспомогательной функции:

def get_key(map, key) when is_map(map) and is_binary(key) do
  case map[key] do
    nil -> map[String.to_atom(key)]
    value -> value
  end
end

Однако это все еще не идеально, поскольку ключ атома может также отсутствовать, и вы получите ноль.

0 голосов
/ 22 января 2020

По моему скромному мнению, самый надежный способ - использовать Map.get/3, который явно проверяет наличие ключа.

other_map = %{email_sended:
  Map.get(my_map, "email_sended", my_map[:email_sended]) 
}
#⇒ %{email_sended: false}

Это может быть сделано даже дважды .

value = 
  Map.get(my_map, "email_sended",
    Map.get(my_map, :email_sended, {:error, :not_found}))
other_map = %{email_sended: value}
#⇒ %{email_sended: false}
...