在 Elixir 中,如何使用 map 变量初始化结构

我知道通过 %User{ email: 'blah@blah.com' }创建结构是可能的。但是,如果我有一个变量 params = %{email: 'blah@blah.com'}是有一种方法来创建这个结构使用这个变量,例如,%User{ params }

这给出了一个错误,只是想知道是否可以爆炸它或其他方式?

23470 次浏览

你应该使用 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"}

另一种使用 Map.merge/2的方法是:

合并(map1,map2)

例如:

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


%User{} |> Map.merge(params)
#=> %User{ email: 'blah@blah.com' }

前面的答案都很好,但有一点需要注意: 结构中的键是原子,散列中的键可能是字符串。使用 struct ()方法只会复制匹配的键,字符串不会与原子匹配。例如:

defmodule User do
defstruct name: "john"
end


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

使用 merge 也很奇怪,因为它会“撤消”Map 的 struct 特性:

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

在长生不老谷歌集团上发现了这个,来自何塞本人:

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