В Elixir, как вы инициализируете структуру с помощью переменной карты

Я знаю, что можно создать структуру через %User{ email: 'blah@blah.com' }, Но если бы у меня была переменная params = %{email: 'blah@blah.com'} Есть ли способ создать эту структуру, используя эту переменную, например, для %User{ params },

Это дает ошибку, просто интересно, можно ли ее взорвать или как-то еще?

3 ответа

Решение

Вы должны использовать struct/2 функция. Из документов:

defmodule User do
  defstruct name: "john"
end

struct(User)
#=> %User{name: "john"}

opts = [name: "meg"]
user = struct(User, opts)
#=> %User{name: "meg"}

struct(user, unknown: "value")
#=> %User{name: "meg"}

Предыдущие ответы оба хороши, с одной оговоркой: ключи в структуре - это атомы, ключи в вашем хэше могут быть строками. Использование метода struct() копирует только совпадающие ключи, а строки не соответствуют атомам. Пример:

defmodule User do
  defstruct name: "john"
end

opts = %{"name" => "meg"}
user = struct(User, opts)
#=> %User{name: "john"}

Использование слияния тоже странно, потому что это "отменит" структурную природу карты:

user = Map.merge(%User{}, opts)
#=> %{:__struct__ => User, :name => "john", "name" => "meg"}

Обнаружил это в Google-группе по эликсиру-языку от самого Хосе:

https://groups.google.com/d/msg/elixir-lang-talk/6geXOLUeIpI/L9einu4EEAAJ

Это в значительной степени способ, за исключением того, что вы можете сделать все за один проход:

def to_struct(kind, attrs) do
  struct = struct(kind)
  Enum.reduce Map.to_list(struct), struct, fn {k, _}, acc ->
    case Map.fetch(attrs, Atom.to_string(k)) do
      {:ok, v} -> %{acc | k => v}
      :error -> acc
    end
  end
end

Еще один способ сделать это с помощью Map.merge/2:

объединить (map1, map2)

Пример:

params
#=> %{email: "blah@blah.com"}

%User{} |> Map.merge(params)
#=> %User{ email: 'blah@blah.com' }
Другие вопросы по тегам