Elixir 更新嵌套映射中的特定值键

问题描述 投票:0回答:4

我输入了以下地图

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}
    ]
  }
}
elixir
4个回答
3
投票

你可以使用

Kernel.put_in/3
这个场景。

map = %{
  nested: %{
    keys: 5
  }
}

put_in(map, [:nested, :keys], 10)
=> %{nested: %{keys: 10}}

1
投票

诀窍是更新一个数组,然后用它的新值更新地图:

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
.


1
投票

你可以这样试试

    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
地图中更新的嵌套列表


0
投票

这是一个使用

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
)
© www.soinside.com 2019 - 2024. All rights reserved.