【问题标题】:Elixir, iterate over nested mapElixir,迭代嵌套地图
【发布时间】:2019-03-09 16:28:55
【问题描述】:

我是 elixir 的新手,我正在尝试从非常嵌套的地图中获取一些文本。

所以我正在对此link 发出获取请求,并使用 Jason.decode 对其进行解码。

我想要做的是遍历它并获取每个文本值(sections->0->content->0->text)。

最后我只希望它是所有文本值的大字符串

(链接可以随时更改,因此可能会有更多地图等)

提前致谢!

【问题讨论】:

  • @GavinBrelstaff 抱歉,我不太明白如何使用您从 elixirforum 发送的解决方案。你能举一个和我的情况类似的例子吗?
  • 我没有给你答案我给你评论了。您的问题在 Elixir 中并非微不足道,因为 json 结构及其深度是可预测的 - 因此您需要找到一种方法将其遍历到每个文本节点并同时收集它们的每个内容。在现实世界中,可能会将整个结构展平,然后挑选出文本节点——elixirforum.com/t/how-to-flatten-a-map/12639/5#post_6
  • 链接已失效,我在问题本身中看不到任何上下文、代码尝试或数据结构。

标签: loops dictionary elixir wikia


【解决方案1】:

您可以使用Enum 和管道运算符|> 来枚举数据结构并提取您想要的部分。

例如:

def pokedex(id) do
  HTTPoison.get!("https://pokemon.fandom.com/api/v1/Articles/AsSimpleJson?id=#{id}")
  |> Map.get(:body)
  |> Jason.decode!()
  |> Map.get("sections")
  |> Enum.reject(fn %{"content" => content} -> content === [] end)
  |> Enum.flat_map(fn %{"content" => content} ->
    content
    |> Enum.filter(fn %{"type" => type} -> type in ["text", "paragraph"] end)
    |> Enum.map(fn %{"text" => text} -> text end)
  end)
  |> Enum.join("\n")
end

细分:

  • Map.get("sections") 选择sections 的内容。
  • Enum.reject(...) 忽略空白部分。
  • Enum.flat_map 遍历各个部分,获取每个部分的 contents,使用内部函数对其进行转换,然后将结果展平为单个列表。
    • Enum.filter(...) 只处理type 属性为textparagraph 的内容。
    • Enum.map 从每个内容映射中提取 text 属性。
  • Enum.join 将生成的字符串列表与换行符连接起来。

【讨论】:

    【解决方案2】:

    Elixir 提供(通过 erlang)一些函数,这些函数可以反映数据结构以检查其类型,例如 is_map/1, is_list/1, is_number/1, is_boolean/1, is_binary/1, is_nil/1 等。来自 docs

    尝试使用您在回复中将使用的常见数据类型。它们可以是基元、映射或基元列表,其中基元类似于布尔值、数字或字符串。

    编写一个函数,尝试递归地遍历你得到的数据结构,直到它到达一个原语,然后返回字符串化的原语

    例如。对于地图,遍历每个值(忽略键),如果该值不是原始值,则使用该节点递归调用函数,直到到达原始值并可以返回字符串化值。列表类似

    这样的事情应该可以工作:

    defmodule Test do
      def stringify(data) do
        cond do
          # <> is concatenation operator
          # -- call stringify() for every value in map and return concatenated string
          is_map(data) -> data |> Map.values |> Enum.reduce("", fn x, acc -> acc <> stringify(x) end)
          # -- call stringify() for every element in list and return concatenated string
          is_list(data) -> data |> Enum.reduce("", fn x, acc -> acc <> stringify(x) end)
          is_boolean(data) -> to_string(data)
          is_number(data) -> to_string(data)
          is_binary(data) -> data # elixir strings are binaries
          true -> ""
        end
      end
    end
    
    # For testing...
    {:ok, %HTTPoison.Response{body: sbody}} = HTTPoison.get("https://pokemon.fandom.com/api/v1/Articles/AsSimpleJson?id=2409")
    {:ok, body} = Poison.decode(sbody)
    Test.stringify(body)
    
    # returns
    "Cyndaquil (Japanese: ヒノアラシ Hinoarashi) is the Fire-type Starter..."
    

    【讨论】:

      【解决方案3】:

      如果可以选择使用外部包来完成此任务,您可以尝试我为此目的编写的包:Iteraptor

      它在嵌套的 Elixir 可枚举术语上提供迭代/映射/减少功能。

      【讨论】:

        猜你喜欢
        • 2013-03-16
        • 2020-12-09
        • 1970-01-01
        • 2023-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多