Эликсир, перебор вложенной карты

#loops #dictionary #elixir #wikia

#циклы #словарь #эликсир #wikia

Вопрос:

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

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

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

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

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

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

Комментарии:

1. Взгляните сюда elixirforum.com/t/pattern-matching-encoded-json/2950 и затем github.com/devinus/poison

2. @GavinBrelstaff Извините, я не совсем понимаю, как использовать решения, которые вы прислали с elixirforum. Не могли бы вы привести мне пример, похожий на мою ситуацию?

3. Я не дал вам ответа, я дал вам комментарий. Ваш вопрос не является тривиальным в Elixir, потому что структура json и ее глубина предсказуемы — таким образом, вам нужно придумать способ обхода его до каждого текстового узла и при сборе каждого из их содержимого. В реальном мире можно было бы сгладить всю структуру, а затем выделить текстовые узлы — elixirforum.com/t/how-to-flatten-a-map/12639/5#post_6

4. Ссылка мертва, и я не вижу никакого контекста, попытки кода или структуры данных в самом вопросе.

Ответ №1:

Вы можете использовать Enum с оператором pipe |> для перечисления структуры данных и извлечения нужных частей.

Например:

 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 or paragraph .
    • 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 и т.д. Из документов

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

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

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

Что-то вроде этого должно сработать:

 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 .

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