web-dev-qa-db-ja.com

Elixirでは、マップ変数で構造体をどのように初期化しますか

%User{ email: '[email protected]' }を介して構造体を作成できることは知っています。しかし、変数params = %{email: '[email protected]'}がある場合、たとえば%User{ params }などの変数を使用してその構造体を作成する方法があります。

これはエラーになりますが、それを爆発させることができるのか、それとも何か他の方法があるのでしょうか?

67
bullfrog

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"}
103
José Valim

前の答えはどちらも良いですが、1つの警告があります。構造体のキーはアトムであり、ハッシュのキーは文字列である可能性があります。 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"}

Jose自身のelixir-lang-talk Googleグループでこれを見つけました。

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

これは、1回のパスですべてを実行できることを除いて、ほとんどの方法です。

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
25
dotdotdotPaul

Map.merge/2 を使用した別の方法:

merge(map1、map2)

例:

params
#=> %{email: "[email protected]"}

%User{} |> Map.merge(params)
#=> %User{ email: '[email protected]' }
16
Sheharyar