如何在 Elixir 中修改地图
How to Modify a Map in Elixir
创建了 JSON api
我的控制器中有一个用于创建操作的端点,它采用 json 如下所示的数据:
[{"opens_detail"=>
[{"ua"=>"Linux/Ubuntu/Chrome/Chrome 28.0.1500.53",
"ip"=>"55.55.55.55",
"ts"=>1365190001,
"location"=>"Georgia, US"}],
"template"=>"example-template",
"metadata"=>{"user_id"=>"123", "website"=>"www.example.com"},
"clicks"=>42,
"ts"=>1365190000,
"state"=>"sent",
"clicks_detail"=>
[{"ua"=>"Linux/Ubuntu/Chrome/Chrome 28.0.1500.53",
"ip"=>"55.55.55.55",
"ts"=>1365190001,
"url"=>"http://www.example.com",
"location"=>"Georgia, US"}],
"email"=>"recipient.email@example.com",
"subject"=>"example subject",
"sender"=>"sender@example.com",
"_id"=>"abc123abc123abc123abc123",
"tags"=>["password-reset"],
"opens"=>42}]
我的目标是利用这个 json 并从中创建一个新的,其中一些键和值被重命名以匹配我下面的模式:
在web/models/messages.ex
...
schema "messages" do
field :sender, :string
field :uniq_id, :string # equal to '_id' in the payload
field :ts, :utc_datetime
field :template, :string
field :subject, :string
field :email, :string
field :tags, {:array, :string}
field :opens, :integer
field :opens_ip, :string # equal to nested 'ip' value in 'open_details'
field :opens_location, :string # equal to nested 'location' value in 'open_details'
field :clicks, :integer
field :clicks_ip, :string # equal to nested 'ip' value in 'click_details'
field :clicks_location, :string # equal to nested 'location' value in 'click_details'
field :status, :string # equal to the "state" in the payload
timestamps()
end
...
这是我试过的:
在web/controller/message_controller.ex:
def create(conn, payload) do
%{ payload |
"uniq_id" => payload["_id"],
"status" => payload["type"]
"open_ips" => Enum.at(payload["opens_detail"], 0)['ip'],
"open_location" => Enum.at(payload["opens_detail"], 0)['location'],
"click_ips" => Enum.at(payload["clicks_detail"], 0)['ip'],
"click_location" => Enum.at(payload["clicks_detail"], 0)['location'],
}
changeset = Message.changeset(%Message{}, payload)
...
end
但很快我就发现它不起作用,因为我仍然需要删除一些密钥。
我来自 Ruby/Python (Rails/Django),不想用我的 OO 知识开始污染我对函数式编程的学习,特别是 elixir/phoenix。
你会如何解决这个问题?
How would you solve this problem?
我会从头开始创建新地图,而不是更新原始地图。您可以使用 get_in
来简化访问嵌套字段的逻辑。这是一个例子:
map = %{
uniq_id: get_in(payload, ["_id"]),
open_ips: get_in(payload, ["opens_detail", Access.at(0), "ip"]),
open_locations: get_in(payload, ["opens_detail", Access.at(0), "location"]),
}
如果要从原始地图中选择字段的子集,可以使用 Map.merge
和 Map.take
:
Map.merge(Map.take(payload, [:sender, ...]), %{uniq_id: ...})
但如果只有几个字段,我宁愿手动写出来。
我的控制器中有一个用于创建操作的端点,它采用 json 如下所示的数据:
[{"opens_detail"=>
[{"ua"=>"Linux/Ubuntu/Chrome/Chrome 28.0.1500.53",
"ip"=>"55.55.55.55",
"ts"=>1365190001,
"location"=>"Georgia, US"}],
"template"=>"example-template",
"metadata"=>{"user_id"=>"123", "website"=>"www.example.com"},
"clicks"=>42,
"ts"=>1365190000,
"state"=>"sent",
"clicks_detail"=>
[{"ua"=>"Linux/Ubuntu/Chrome/Chrome 28.0.1500.53",
"ip"=>"55.55.55.55",
"ts"=>1365190001,
"url"=>"http://www.example.com",
"location"=>"Georgia, US"}],
"email"=>"recipient.email@example.com",
"subject"=>"example subject",
"sender"=>"sender@example.com",
"_id"=>"abc123abc123abc123abc123",
"tags"=>["password-reset"],
"opens"=>42}]
我的目标是利用这个 json 并从中创建一个新的,其中一些键和值被重命名以匹配我下面的模式:
在web/models/messages.ex
...
schema "messages" do
field :sender, :string
field :uniq_id, :string # equal to '_id' in the payload
field :ts, :utc_datetime
field :template, :string
field :subject, :string
field :email, :string
field :tags, {:array, :string}
field :opens, :integer
field :opens_ip, :string # equal to nested 'ip' value in 'open_details'
field :opens_location, :string # equal to nested 'location' value in 'open_details'
field :clicks, :integer
field :clicks_ip, :string # equal to nested 'ip' value in 'click_details'
field :clicks_location, :string # equal to nested 'location' value in 'click_details'
field :status, :string # equal to the "state" in the payload
timestamps()
end
...
这是我试过的:
在web/controller/message_controller.ex:
def create(conn, payload) do
%{ payload |
"uniq_id" => payload["_id"],
"status" => payload["type"]
"open_ips" => Enum.at(payload["opens_detail"], 0)['ip'],
"open_location" => Enum.at(payload["opens_detail"], 0)['location'],
"click_ips" => Enum.at(payload["clicks_detail"], 0)['ip'],
"click_location" => Enum.at(payload["clicks_detail"], 0)['location'],
}
changeset = Message.changeset(%Message{}, payload)
...
end
但很快我就发现它不起作用,因为我仍然需要删除一些密钥。
我来自 Ruby/Python (Rails/Django),不想用我的 OO 知识开始污染我对函数式编程的学习,特别是 elixir/phoenix。
你会如何解决这个问题?
How would you solve this problem?
我会从头开始创建新地图,而不是更新原始地图。您可以使用 get_in
来简化访问嵌套字段的逻辑。这是一个例子:
map = %{
uniq_id: get_in(payload, ["_id"]),
open_ips: get_in(payload, ["opens_detail", Access.at(0), "ip"]),
open_locations: get_in(payload, ["opens_detail", Access.at(0), "location"]),
}
如果要从原始地图中选择字段的子集,可以使用 Map.merge
和 Map.take
:
Map.merge(Map.take(payload, [:sender, ...]), %{uniq_id: ...})
但如果只有几个字段,我宁愿手动写出来。