Jagdish Narayandasani
Jagdish Narayandasani

Reputation: 772

Multiple if condition in elixir

How can I implement multiple if conditions in my code like below

person = %Person{}

if x.name == "Test" do 
  person = %{person | name: "Test"}
end

if x.age == 50 do 
 person = %{person | age: 50}
end

here "x" is another object where I am trying to validate data and then updating person struct accordingly. I already wrote code as above but got a below warning

the variable "person" is unsafe as it has been set inside one of: case, cond, receive, if, and, or, &&, ||. Please explicitly return the variable value instead. For example:

case integer do
  1 -> atom = :one
  2 -> atom = :two
end

should be written as

atom =
  case integer do
    1 -> :one
    2 -> :two
  end

Upvotes: 1

Views: 4636

Answers (3)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Everything in Elixir is immutable. I would suggest you to read this great writing by José Valim on rebinding variables in Elixir.

The functionality you are after looks mostly as a reducing the struct on a list of fields, that’s why I probably would go with Enum.reduce/3:

defmodule Person, do: defstruct ~w|foo name age|a
x = [name: true, age: false] # flags

~w|name age|a
|> Enum.zip(['Test', 50])
|> Enum.reduce(%Person{foo: :bar}, fn {field, value}, acc ->
     if x[field], do: %{acc | field => value}, else: acc
   end)
#⇒ %Person{age: nil, foo: :bar, name: 'Test'}

Upvotes: 2

intentionally-left-nil
intentionally-left-nil

Reputation: 8274

When you are matching multiple clauses to validate a field, I would combine functions that validate the pieces, as follows:

defmodule PersonChecker do
  def validate(x, person) do
    person
    |> validate_name(x)
    |> validate_age(x)
  end

  defp validate_name(person, %{name: name}) when name == "Test" do
    %{person | name: "Test"}
  end
  defp validate_name(person, _x), do: person

  defp validate_age(person, %{age: age}) when age == 50 do
    %{person | age: 50}
  end
  defp validate_age(_x, person), do: person
end

If you wanted to use case instead of multiple validate_name functions it would look like this:

defp validate_name(person, x) do
  case x.name do
    "Test" -> %{person | name: "Test"}
    _ -> person
  end
end

As an aside, depending on your use case, it might be easier to muck with x to strip out the fields you don't want, and then convert x into a struct using the struct(Person, x) function.

Upvotes: 3

Grych
Grych

Reputation: 2901

You can't rebind the variable inside the block. As the error message suggest, set it explicit:

person = %Person{}
person = if x.name == "Test", do: %{person | name: "Test"}, else: person
person = if x.age == 50, do: %{person | age: 50}, else: person

Even if variables are immutable, Elixir allows rebinding, but it would be easier to understand if it would not be allowed (like in Erlang). In this case, you would have to write something like:

person = %Person{}
person_with_name = if x.name == "Test", do: %{person | name: "Test"}, else: person
person_with_name_and_age = if x.age == 50, do: %{person_with_name | age: 50}, else: person_with_name

Upvotes: 2

Related Questions