我遇到了这样的问题,我真的不知道如何正确解决这个问题。我需要创建给定地图的路径列表。路径列表应由元组组成,包括通向给定值的键列表和值本身,如下例所示。
示例输入:
from bs4 import BeautifulSoup
from PIL import Image
import requests
示例输出:
%{
foo: %{
bar: %{
value: "y"
},
car: %{
value: "x"
}
},
second: "level"
}
我尝试实现它的方法是使用带有 [
{ [:foo, :bar, :value], "y" },
{ [:foo, :car, :value], "x" },
{ [:second], "level"}
]
的递归函数,遍历映射中的所有键和值。
Enum.reduce()/3
我感觉这是解决这个问题的正确方向,但我仍然不知道如何从这一点走得更远。上面给出的这个函数的输出是:
def reduce_map(paths_map, nest) do
Enum.reduce(paths_map, nest, fn {key, val}, acc ->
case typeof(val) do
"map" ->
acc ++ [reduce_map(val, [key])]
_ ->
acc ++ [key]
end
end)
end
我希望有人能帮我解决这个问题:) 干杯
答案 0 :(得分:2)
你确实是在正确的方向。但是请注意,您将 val
传递给递归函数调用,但是当 val
不是地图时,您没有将其包含在结果中。
此外,为了避免嵌套键被分组在父项下之类的事情,就像它似乎发生在您的结果中一样,您可能可以使用额外的参数来跟踪路径中的键,直到嵌套中的某个键地图。
这是一个使用我刚才提到的方法的解决方案,但它起草得非常快,我相信它相当幼稚,可以在很多方面进行改进:
defmodule Test do
def reduce(map, paths, accumulated_paths) do
Enum.reduce(map, paths, fn {key, val}, acc when is_map(val) ->
acc ++ [reduce(val, paths, accumulated_paths ++ [key])]
{key, val}, acc ->
acc ++ [{accumulated_paths ++ [key], val}]
end)
end
end
然后在 iex 中:
iex> m = %{
...> foo: %{
...> bar: %{
...> value: "y"
...> },
...> car: %{
...> value: "x"
...> }
...> },
...> second: "level"
...> }
%{foo: %{bar: %{value: "y"}, car: %{value: "x"}}, second: "level"}
iex> Test.reduce(m, [], []) |> List.flatten()
[{[:foo, :bar, :value], "y"}, {[:foo, :car, :value], "x"}, {[:second], "level"}]
如您所见,它产生了预期的结果,但是,例如,它需要调用 List.flatten
,因为添加了嵌套列表
答案 1 :(得分:2)
我编写了库来深度迭代嵌套结构 Iteraptor
。
如果您对第 3 方没问题,那就很简单了:
Iteraptor.each(input, &IO.inspect/1)
{[:foo, :bar, :value], "y"}
{[:foo, :car, :value], "x"}
{[:second], "level"}
#⇒ %{foo: %{bar: %{value: "y"}, car: %{value: "x"}},
# second: "level"}
它是 OSS,因此您可以查看来源以获取更多见解。此外,它还提供 map/2
、reduce/3
等。
答案 2 :(得分:2)
当您开始使用嵌套时,我发现很难对 Enum.reduce
进行推理,并且通常最终只是使用带有递归的基本函数来处理这样的事情:
defmodule Example do
def reduce_map(tree) do
tree |> reduce_map([]) |> List.flatten()
end
def reduce_map(tree, path) when is_map(tree) do
for {k, v} <- tree, do: reduce_map(v, [k | path])
end
def reduce_map(value, path), do: {Enum.reverse(path), value}
end
IEx:
iex(1)> Example.reduce_map(%{foo: %{bar: %{value: "y"}, car: %{value: "x"}}, second: "level"})
[{[:foo, :bar, :value], "y"}, {[:foo, :car, :value], "x"}, {[:second], "level"}]