Skip to content

Instantly share code, notes, and snippets.

@ervinne13
Last active April 22, 2023 13:41
Show Gist options
  • Save ervinne13/2e7de9381886c0178261251f60eb4a57 to your computer and use it in GitHub Desktop.
Save ervinne13/2e7de9381886c0178261251f60eb4a57 to your computer and use it in GitHub Desktop.
A simple function that generates an unordered list based on a tree of nodes.
/**
Instructions:
You may just copy the whole code and execute this on the browser console or include this to a node
project that has [JSDOM](https://github.com/jsdom/jsdom) to run it in the terminal.
Requirements:
A node is either a string, or a JS object of the form {"name": "a name", "nodes": [more, nodes]}
Write a function in javascript that converts an array of nodes into an unordered list in HTML
function mySample(arrayOfNodes) {...; return html}
For example:
["one", {name: "two", nodes: [{name: "2.1", nodes: ["2.1a")}, "two point two"|}, "three"]
=>
• one
• two
• 2.1
• 2.1a
• two point two
• three
Answer: This can resolved with simple recursion. We can either manually build the HTML string,
or continuously add nodes of element objects to the tree. I'm more comfortable doing the latter
if we're dealing with vanilla.
*/
/**
* Creates an unordered list element object based on the array of nodes.
* An HOF that contains recursive functionality inside.
*
* @param {String|Object} nodes
* @param {Object} parent Element that can either be the unordered list, or
* @returns
*/
const createListElement = (nodes = [], parent = null) => {
const SAFETY_LIMIT = 100; // Arbitrary count, but I'm just more comfortable to always use a recursion stopper in case I mess up when developing.
let recursionCount = 0;
const recursiveCreateUL = (nodes = [], parent = null) => {
assertInfiniteRecursionSafe(nodes);
if (!parent) {
return recursiveCreateUL(nodes, document.createElement("ul"));
}
for (const node of nodes) {
let li;
if (typeof node === "object") {
li = nodeObjToLi(node);
} else if (typeof node === "string") {
li = stringToLi(node);
} else {
const e = new Error("Invalid node. Nodes can only be strings or objects");
e.node = node;
throw e;
}
parent.appendChild(li);
}
return parent;
};
const nodeObjToLi = (node) => {
assertNodeObjectIsValid(node);
const li = document.createElement("li");
li.innerHTML = node.name;
li.appendChild(recursiveCreateUL(node.nodes));
return li;
}
const stringToLi = (text) => {
const li = document.createElement("li");
li.innerHTML = text;
return li;
}
const assertInfiniteRecursionSafe = (nodes) => {
if (recursionCount++ >= SAFETY_LIMIT) {
// Ideally I have at least a generic Error object I can put other variables on but it's kinda
// overkill for now, I really just want to include the nodes/node that caused the fault so
// let's just do it this way for now.
const e = new Error("Recursion limit reached. A faulty node list might have been provided.");
e.nodes = nodes;
throw e;
}
}
const assertNodeObjectIsValid = (node) => {
if (!(node?.name && Array.isArray(node?.nodes))) {
const e = new Error("Node object is invalid. It must have a name and an array of nodes");
e.node = node;
throw e;
}
}
return recursiveCreateUL(nodes, parent);
};
try {
// Try to change the input too, or maybe deliberately break it by passing non object, sudden arrays, or just incorrect object keys
createListElement(["one", { name: "two", nodes: [{name: "2.1", nodes: [ "2.1a" ] }, "two point two" ] }, "three"]);
} catch(e) {
// things we can report to the user
console.error(e);
// things we keep only on our internal logs, but i'm outputting everything here as we don't have
// a logging system in place
if (e.node) {
console.error({ node: e.node })
}
if (e.nodes) {
console.error({ nodes: e.nodes })
}
}
@ervinne13
Copy link
Author

ervinne13 commented Apr 22, 2023

"Show or talk about how to enhance the code to change the bullet symbols to reflect the level of the item."

With the way the function is designed, it's very simple, just add an ol element as the parent instead of using a null default:

createListElement(["one", { name: "two", nodes: [{name: "2.1", nodes: [ "2.1a" ] }, "two point two" ] }, "three"], document.createElement("ol"));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment