如何在嵌套的多个地图中放置一个值(phoenix params)

How to put a value in multiple maps that are nested (phoenix params)

纯灵药问题:如何在嵌套在主地图中的地图中添加值?
嵌套地图的数量不固定。

在 Phoenix 上下文中,问题是如何在参数和嵌套参数中增加值。

如何从这里开始:

%{
  "fname" => "",
  "lname" => "",
  "addresses" => %{
    "0" => %{
      "address" => ""
    }
  },
  "phone_numbers" => %{
    "0" => %{"number" => ""},
    "1" => %{"number" => ""},
    "2" => %{"number" => ""},
  },
}

至:

%{
  "account_id" => 2,
  "fname" => "",
  "lname" => "",
  "addresses" => %{
    "0" => %{
      "account_id" => 2,
      "address" => ""
    }
  },
  "phone_numbers" => %{
    "0" => %{"account_id" => 2,"number" => ""},
    "1" => %{"account_id" => 2,"number" => ""},
    "2" => %{"account_id" => 2,"number" => ""},
  },
}

"account_id" => 2 已添加到主值和嵌套值。

参数可能有 "Phoenix" 方式,但我也在尝试考虑某种方式 Map.merge()递归。

编辑:

披露:lens 的作者在这里。

我想不出只用内置 Access 来实现的方法,但你可以用 lens 库 (https://hex.pm/packages/lens) 实现:

data = %{
  "fname" => "",
  "lname" => "",
  "addresses" => %{
    "0" => %{
      "address" => ""
    }
  },
  "phone_numbers" => %{
    "0" => %{"number" => ""},
    "1" => %{"number" => ""},
    "2" => %{"number" => ""}
  }
}

data
|> put_in(
  [
    Lens.both(
      Lens.root(),
      Lens.keys(["addresses", "phone_numbers"]) |> Lens.map_values()
    )
    |> Lens.key("account_id")
  ],
  2
)
|> IO.inspect()

# => 
%{
  "account_id" => 2,
  "addresses" => %{"0" => %{"account_id" => 2, "address" => ""}},
  "fname" => "",
  "lname" => "",
  "phone_numbers" => %{
    "0" => %{"account_id" => 2, "number" => ""},
    "1" => %{"account_id" => 2, "number" => ""},
    "2" => %{"account_id" => 2, "number" => ""}
  }
}

Lens.both(
  Lens.root(),
  Lens.keys(["addresses", "phone_numbers"]) |> Lens.map_values()
)
|> Lens.key("account_id")

是关键位。它基本上说 "take every value under the key account_id FROM ((the whole data) AND (every map value in maps located under the keys addresses and phone_numbers))".

编辑:

OP 在评论中提到密钥可能无法预先知道。您可以在不明确指定它们的情况下实现相同的效果:

Lens.both(
  Lens.root(),
  Lens.map_values() |> Lens.filter(&is_map/1) |> Lens.map_values()
)
|> Lens.key("account_id")

如果您不介意 "account_id" 出现在中间映射中,最简单的方法可能是像这样编写一个递归函数:

defmodule AccountId do
  def add(map, account_id) do
    for {k, v} <- map, into: %{} do
      if is_map(v) do
        {k, add(v, account_id)}
      else
        {k, v}
      end
    end
    |> Map.merge(%{"account_id" => account_id})
  end
end

AccountId.add(data, 2) |> IO.inspect() # =>
%{
  "account_id" => 2,
  "addresses" => %{
    "0" => %{"account_id" => 2, "address" => ""},
    "account_id" => 2
  },
  "fname" => "",
  "lname" => "",
  "phone_numbers" => %{
    "0" => %{"account_id" => 2, "number" => ""},
    "1" => %{"account_id" => 2, "number" => ""},
    "2" => %{"account_id" => 2, "number" => ""},
    "account_id" => 2
  }
}