Эликсир, перебери вложенную карту - PullRequest
0 голосов
/ 09 марта 2019

Я новичок в elixir и пытаюсь получить текст с очень вложенной карты.

Итак, я делаю запрос get на эту ссылку и декодирую его с помощьюJason.decode.

Что я хочу сделать, это перебрать его и получить каждое текстовое значение (section-> 0-> content-> 0-> text).

В конце япросто хочу, чтобы это была большая строка всех текстовых значений

(ссылка всегда может быть изменена, поэтому может быть больше карт и т. д.)

Заранее спасибо!

Ответы [ 3 ]

0 голосов
/ 10 марта 2019

Вы можете использовать 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 которого равно text или paragraph.
    • Enum.map извлекает свойство text из каждой карты содержимого.
  • Enum.join объединяет результирующий список строк с символом новой строки.
0 голосов
/ 10 марта 2019

Если использование внешнего пакета для этой задачи является опцией, вы можете попробовать пакет, который я написал, именно для этой цели: Iteraptor.

Он обеспечивает итерацию/ map / уменьшить функциональность над вложенным эликсиром перечисляемыми терминами.

0 голосов
/ 09 марта 2019

Elixir предоставляет (через erlang) некоторые функции, которые могут отражать структуры данных для проверки их типа, например is_map/1, is_list/1, is_number/1, is_boolean/1, is_binary/1, is_nil/1 и т. Д. Из документов

Попробуйте использовать общие типы данных, которые вы будете иметь в своем ответе. Это могут быть примитивы, карты или списки примитивов, в которых примитивы похожи на логические, числовые или строковые.

Напишите функцию, которая пытается рекурсивно пройти через полученную структуру данных, пока не достигнет примитива, а затем вернуть примитив со строковым значением

Ex. для карт просмотрите каждое значение (ключ игнорирования) и, если значение не является примитивом, рекурсивно вызывайте функцию с этим узлом, пока не достигнете примитива и не сможете вернуть строковое значение. Аналогично для списков

Примерно так должно работать:

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..."
...