aphillipo

aphillipo

Excerpt implementation for Floki

Hello there! I’ve been working on an Excerpt implementation on top of Floki that greedily includes tags. I’d love for the next step to be able to preserve tags but this is a good starting point; any advice on that welcome.

Let me know if there is anything I could improve or if there are any performance issues I’m encountering by using recursion for this. It’s also worth noting this is an adaption
of the DeepText search that comes with Floki for my own needs.

defmodule PayPip.Text do
  @moduledoc """
  PayPip.Text is a strategy to get text nodes from a HTML tree using a deep search
  algorithm limited to a certain length + the full content of the final node. It will 
  get all string nodes and concat them into an accumulator up to that point.
  """

  @type html_tree :: tuple | list

  @doc """
  Get text nodes of a desired length + final node length.
  ## Examples
      iex> PayPip.Text.get([{"a", [], ["The meaning of life is...", {"strong", [], ["something else"]}] }], 5)
      {"The meaning of life is...", 20}
      iex> PayPip.Text.get([{"a", [], ["The meaning of life is...", {"strong", [], ["something else"]}] }], 140, " ")
      {"The meaning of life is... something else", -101}
  """
  @spec get(html_tree, number, binary) :: {binary, number}
  def get(html_tree, max_length \\ 255, sep \\ "") do
    get_text(html_tree, {"", max_length}, sep)
  end
  
  # initial piece of text
  defp get_text(text, {"", max_length}, _sep) when is_binary(text), do: {text, max_length - String.length(text)}
  # all other text
  defp get_text(text, {acc, max_length}, sep) when is_binary(text) do
    {Enum.join([acc, text], sep), max_length - String.length(text)}
  end
  # deal with nodes lists and sort out over count to be correct (negative when under original max_length)
  defp get_text(nodes, {acc, max_length}, sep) when is_list(nodes) do
    Enum.reduce_while nodes, {acc, max_length}, fn(child, istr) ->
      if max_length > 0 do
        {final_acc, final_length} = get_text(child, istr, sep)
        {:cont, {final_acc, -final_length}}
      else
        {:halt, {acc, -max_length}}
      end
    end
  end
  # ignore comments
  defp get_text({:comment, _}, {acc, max_length}, _), do: {acc, max_length}
  # turn BR tags into new lines
  defp get_text({"br", _, _}, {acc, max_length}, _), do: {acc <> "\n", max_length - 1}
  # process children
  defp get_text({_, _, nodes}, acc, sep) do
    get_text(nodes, acc, sep)
  end
end

Where Next?

Popular in Discussions Top

CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -&gt; H; nth(N, [_|T]) when N &gt; 1 -&gt; nth(N - 1, T). Elixir Enum.at … coo...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40082 209
New
slashdotdash
Phoenix Live View is now publicly available on GitHub. Here’s Chris McCord’s tweet announcing making it public.
New
eteeselink
Hi all, In the last days, two things happened: A blog post titled “They might never tell you it’s broken” made the rounds. It’s about ...
New
Fl4m3Ph03n1x
Background This question comes mainly from my ignorance. Today is Black Friday, one of my favorite days of the year to buy books. One boo...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New

Other popular topics Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement