从地图创建路径

时间:2021-02-02 12:41:02

标签: elixir

我遇到了这样的问题,我真的不知道如何正确解决这个问题。我需要创建给定地图的路径列表。路径列表应由元组组成,包括通向给定值的键列表和值本身,如下例所示。

示例输入:

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

我希望有人能帮我解决这个问题:) 干杯

3 个答案:

答案 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/2reduce/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"}]
相关问题