Skip to content

Instantly share code, notes, and snippets.

@joeljuca
Created April 27, 2022 21:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joeljuca/37ab9888c51499fc9793645ebe5cdfaf to your computer and use it in GitHub Desktop.
Save joeljuca/37ab9888c51499fc9793645ebe5cdfaf to your computer and use it in GitHub Desktop.
# Snippet provided by the Slab Engineering Team
input = [
%{"text" => "One", "indent" => 0, "type" => "ordered"},
%{"text" => "Two", "indent" => 0, "type" => "ordered"},
%{"text" => "Alpha", "indent" => 1, "type" => "bullet"},
%{"text" => "Beta", "indent" => 1, "type" => "bullet"},
%{"text" => "I", "indent" => 2, "type" => "ordered"},
%{"text" => "II", "indent" => 2, "type" => "ordered"},
%{"text" => "Three", "indent" => 0, "type" => "ordered"}
]
#
# My solution is right down below
# PS: this web-based code editor is really hard to work with! :'(
#
defmodule RichText do
# - [x] come up with a nested data structure to
# better represent a tree of HTML nodes
# - [x] parse nested data structure into HTML
# - [ ] convert linear data structure into nested
# - [ ] bonus: handle indentation?
# @node_tree_sample %{
# type: :ol,
# children: [
# %{
# type: :li,
# children: [
# %{ type: :text, text: "One" },
# ]
# },
# %{
# type: :ul,
# children: [
# %{
# type: :li,
# children: [
# %{ type: :text, text: "Alpha" }
# ]
# },
# ]},
# ]
# }
def from_linear(linear) do
linear
|> Enum.reduce([], fn item, acc ->
# group items by indentation
case acc do
[] ->
[[item]]
[last_group | tail] ->
[%{"indent" => last_indent} | _] = last_group
if item["indent"] == last_indent,
do: [[item | last_group] | tail],
else: [[item] | acc]
end
end)
|> Enum.reverse()
|> IO.inspect(label: :list)
# |> Enum.reduce(nil, fn item_group, acc ->
# # IO.inspect(item_group)
# case acc do
# nil -> convert_to_node(item_group)
# # [%{} | _] -> convert_to_node(item_group)
# %{children} = -> acc
# end
# end)
# |> Enum.map(fn [%{"indent" => indent} | _] = item_group ->
# # turn linear data structure into a nested one,
# # to later be used by parse/1
# case item_group do
# [] -> []
# [%{} | _] -> convert_to_node(item_group)
# end
# end)
end
@linear_types_to_node_types %{
"ordered" => :ol,
"bullet" => :ul
}
defp convert_to_node(item_group) do
[%{"type" => linear_type, "indent" => indent} | _] = item_group
node_type =
item_group
|> List.first()
|> Map.get("type")
|> then(&Map.get(@linear_types_to_node_types, &1))
%{
type: node_type,
indent: indent,
children:
Enum.map(item_group, fn item ->
%{type: :li, children: [%{type: :text, text: item["text"]}]}
end)
}
end
@doc """
Turn a DOM node into its equivalent HTML
"""
def parse(%{type: :text} = node), do: node.text
def parse(%{type: type} = node)
when type in [:li, :ol, :ul] do
children_html =
node.children
|> Enum.map(&parse/1)
|> Enum.join("\n")
"<#{type}>#{children_html}</#{type}>"
end
def parse(invalid) when invalid in [nil, []], do: ""
end
# Quick-and-dirty tests ftw!
# PS: no time for ExUnit tests in just 90 min =(
# RichText.parse(%{
# type: :ul,
# children: [
# %{
# type: :li,
# children: [
# %{ type: :text, text: "Shopping list" },
# ]
# },
# %{
# type: :ol,
# children: [
# %{
# type: :li,
# children: [%{ type: :text, text: "Food" }]
# },
# %{type: :li, children: [%{ type: :text, text: "Beer" }]},
# %{type: :li, children: [%{ type: :text, text: "More beer" }]},
# ]},
# ]
# })
# |> IO.inspect()
RichText.from_linear(input)
|> IO.inspect(label: :output)

Hello 👋

Hi there, thank you for taking the time to interview with us today. This question is abstracted from our codebase, and we hope you will enjoy solving this problem.

The question might be harder than your initial reaction. Please read everything in this instruction carefully.

Good luck!

Problem

Rich text in Slab is represented in a JSON-based format for consistency and specificity. A simplified version for a nested list might look something like this:

[
{ "text": "One", "indent": 0, "type": "ordered" },
{ "text": "Two", "indent": 0, "type": "ordered" },
{ "text": "Alpha", "indent": 1, "type": "bullet" },
{ "text": "Beta", "indent": 1, "type": "bullet" },
{ "text": "I", "indent": 2, "type": "ordered" },
{ "text": "II", "indent": 2, "type": "ordered" },
{ "text": "Three", "indent": 0, "type": "ordered" }
]

Which the front end will render to something like:

  1. One
  2. Two • Alpha • Beta i. I ii. II
  3. Three

It is sometimes necessary to convert this JSON representation into HTML for exporting, emails, or pasting into other applications.

Your task is to write a program that handles this conversion. The above output would look like this:

<ol>
  <li>One</li>
  <li>Two
    <ul>
      <li>Alpha</li>
      <li>Beta
        <ol>
          <li>I</li>
          <li>II</li>
        </ol>
      </li>
    </ul>
  </li>
  <li>Three</li>
</ol>

Submission

Your main function should be called deltaToHtml. It should accept an array/list as the input, and returns a string as the output. Input

You do not need to parse a JSON string. You can assume the input is already an array. In order, each item in the array represents an item in the list. Each item has 3 attributes:

text: the pure text content to be displayed in the item
indent: item's indent level. You can assume indent will at most increase by 1 from one item to its next item.
type: what type of list is the item in, the possible values are:
    bullet: <ul> type list
    ordered: <ol> type list

Output

The HTML presentation of the input.

Notes:

In our examples, we included indentations for readability. Your submission does not need to output any white space.
According to the HTML spec, a nested ol/ul element needs to be contained in a li element. For example, both of the following examples are incorrect:

<-- Invalid HTML syntax -->

<ol>
  <li>One</li>
  <li>Two</li>
  <ul>
    <li>Alpha</li>
    <li>Beta</li>
  </ul>
  <li>Three</li>
</ol>

<-- Valid HTML syntax, but does NOT represent an expected nested list -->

<ol>
  <li>One</li>
  <li>Two</li>
  <li>
    <ul>
      <li>Alpha</li>
      <li>Beta</li>
    </ul>
  </li>
  <li>Three</li>
</ol>

Additional Info

  • You may choose any language to implement your program by changing the language setting in CoderPad.
  • You have limited time. We do care about code quality, but correctness comes first.
  • If you have extra time, you could use it on testing, improving readability, writing docs, etc.
  • http://htmlformatter.com can be helpful for reading the output.
  • If you have any questions, please email chengyin@slab.com.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment