我输入了以下地图
temp = %{
"temperature" => %{
"details" => "Temperature Template",
"sensors" => [
%{
"name" => "TMP",
"type" => "integer",
"value" => 0
},
%{
"name" => "DEEP_SLEEP",
"type" => "integer",
"value" => 10
}
]
}
}
我现在需要更新密钥
"value" => 10
从 10 到 15 与密钥 "name" => "DEEP_SLEEP"
从更新嵌套地图开始
new_sensor =
temp
|> Map.get("temperature")
|> Map.get("sensors")
|> Enum.find(fn x -> x["name"] == "DEEP_SLEEP" end)
|> Map.put("value", 15)
%{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 15}
现在想升一级
new_device =
temp
|> Map.get("temperature")
|> Map.put("sensors", %{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 15})
%{
"details" => "Temperature Template",
"sensors" => %{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 15}
}
现在我应该可以用类似的东西重建地图了:
Map.put(temp, "temperature", new_device)
但结果不正确
iex(32)> Map.put(temp, "temperature", new_device)
%{
"temperature" => %{
"details" => "Temperature Template",
"sensors" => %{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 15}
}
}
iex(33)> temp
%{
"temperature" => %{
"details" => "Temperature Template",
"sensors" => [
%{"name" => "TMP", "type" => "integer", "value" => 0},
%{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 10}
]
}
}
Kernel.put_in/3
这个场景。
map = %{
nested: %{
keys: 5
}
}
put_in(map, [:nested, :keys], 10)
=> %{nested: %{keys: 10}}
诀窍是更新一个数组,然后用它的新值更新地图:
iex|1 ▶ old_value = temp["temperature"]["sensors"]
#⇒ [
# %{"name" => "TMP", "type" => "integer", "value" => 0},
# %{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 10}
# ]
我们将显式地对函数参数进行模式匹配,以仅更新具有
"DEEP_SLEEP"
名称的地图。
iex|2 ▶ new_value = Enum.map(old_value, fn
...|2 ▷ %{"name" => "DEEP_SLEEP"} = subj ->
...|2 ▷ %{subj | "value" => subj["value"] + 5}
...|2 ▷ subj -> subj
...|2 ▷ end)
#⇒ [
# %{"name" => "TMP", "type" => "integer", "value" => 0},
# %{"name" => "DEEP_SLEEP", "type" => "integer", "value" => 15}
# ]
Kernel.put_in/3
更新原始地图:
iex|3 ▶ put_in(temp, ~w|temperature sensors|, new_value)
Kernel.get_and_update_in/3
.
你可以这样试试
put_in(
temp,
["temperature", "sensors"],
update_in(
temp["temperature"]["sensors"],
[Access.filter(&(&1["name"] == "DEEP_SLEEP")), "value"],
fn _ -> 15 end)
)
1 首先你
update_in
嵌套值,然后你 put_in
地图中更新的嵌套列表
update_in/2
的版本:
update_in(temp["temperature"]["sensors"], fn items ->
Enum.map(items, fn
%{"name" => "DEEP_SLEEP", "value" => value} = item -> %{item | "value" => value + 5}
item -> item
end)
end)
另一个,灵感来自@MoVod对
Access.filter
的使用:
update_in(
temp,
[
Access.key!("temperature"),
Access.key!("sensors"),
Access.filter(&match?(%{"name" => "DEEP_SLEEP"}, &1))
],
fn %{"value" => value} = item -> %{item | "value" => value + 5} end
)