DEV Community

Berra
Berra

Posted on

1

Recursion recipe

I think recursion is really hard. It’s not like it's really hard. It’s just that I kind of always has to redo my functions many times before I get it right.

So I will try to make a small recipe for doing recursion on a nested data structure.

Say we have this data:

[{
  label: "i am root",
  href: "/",
  children: [{
    label: "i am a leaf",
    value: "/leafnode-1",
    children: null
  }]
}]
Enter fullscreen mode Exit fullscreen mode

And maybe we want to turn this into a navigation or something like this:

<ul>
  <li>
    <a href="/">i am root</a>
    <ul>
      <li><a href="/leafnode-1">i am a leaf</a></li>
    </ul>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

And this is our output functions. First the outer function that will render the <ul> element in our example. It will take a node and a function to render the list.

function renderList(node, innerFn) {
  return `<ul>${innerFn(node).join("")}</ul>`;
}
Enter fullscreen mode Exit fullscreen mode

The inner function (innerFn) will return an array with the output of our next function.

And so. This is the function to render the list. It will take an item as argument and the outer function to render a sublist. And so on.

function renderItems(item, outerFn) {
  return `
    <li><a href="${item.href}">${item.label}</a>
      ${outerFn(item.children)}
    </li>
  `;
}
Enter fullscreen mode Exit fullscreen mode

And this is the "behind the scenes" function that connects the functions and does the mapping.

function createRecursionFunction({ 
  innerFn,
  outerFn,
  renderLeaf,
  isLeafNode 
}) {
  function _outerFn(node) {
    if (isLeafNode(node)) {
      return renderLeaf(node);
    }
    return outerFn(node, _innerFn);
  }
  function _innerFn(node) {
    return node.map(function mapper(i) {
      return innerFn(i, _outerFn)
    });
  }
  return function(data) {
    return _outerFn(data);
  };
}
Enter fullscreen mode Exit fullscreen mode

Now we can use our factory to create a brand new function. But first let's make a function to check if we’re in a leafnode and render an empty string if that is the case.

function isLeafNode(node) {
  return node === null;
}

function renderLeaf() {
  return '';
}
Enter fullscreen mode Exit fullscreen mode
const createMenu = createRecursionFunction({
  outerFn: renderList,
  innerFn: renderItems,
  renderLeaf: renderLeaf,
  isLeafNode: isLeafNode,
});

const htmlMenu = createMenu(data);
Enter fullscreen mode Exit fullscreen mode

Now we have a flexible recursion function factory that will take your data an inner and an outer function as arguments.

I haven't used this in production myself. But I imagine it could be handy.

Until next time.

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more