0

I'm trying to find URLs in a nested JSON response and map them. My function so far looks like this:

def list(env, id) do
  Service.get_document(env, id)
  |> Poison.decode!
  |> Enum.find(fn {_key, val} -> String.starts_with?(val, 'https') end)
end

The JSON looks roughly like this:

  "stacks": [
    {
      "boxes": [
        {
          "content": "https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
          "box": "photo"
        }
      ]
    }
  ],
  "logo": "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"

So URLs can have any key, and be at any level.

With that code I get this error:

no function clause matching in String.starts_with?/2

Anyone got a better way to find in JSON responses?

t56k
  • 6,769
  • 9
  • 52
  • 115

1 Answers1

3

You'll have to use recursive function for this, which handles three types of data:

  1. For map, it recurses over all its values.
  2. For list, it recurses over all its elements.
  3. For string, it selects strings that start with "https"

Here's a simple implementation which accepts a term and a string to check with starts_with?:

defmodule A do
  def recursive_starts_with(thing, start, acc \\ [])

  def recursive_starts_with(binary, start, acc) when is_binary(binary) do
    if String.starts_with?(binary, start) do
      [binary | acc]
    else
      acc
    end
  end
  def recursive_starts_with(map, start, acc) when is_map(map) do
    Enum.reduce(map, acc, fn {_, v}, acc -> A.recursive_starts_with(v, start, acc) end)
  end
  def recursive_starts_with(list, start, acc) when is_list(list) do
    Enum.reduce(list, acc, fn v, acc -> A.recursive_starts_with(v, start, acc) end)
  end
end

data = %{
  "stacks" => [
    %{
      "boxes" => [
        %{
          "content" => "https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
          "box" => "photo"
        }
      ]
    }
  ],
  "logo" => "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"
}

data |> A.recursive_starts_with("https") |> IO.inspect

Output:

["https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
 "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"]
Dogbert
  • 212,659
  • 41
  • 396
  • 397