In working with the Slate framework for building rich text editors recently, I
found myself faced with a scenario where I needed to loop through the editor’s
value.
Turn out, the value that comes out the editor is an array of objects that is nestable, I assume infinitely, by way of the children
property.
Because I wasn’t entirely certain about the depth of the data nesting, I decided the best approach would be to write a function that could loop through and travel through the levels.
I’m not going to get into the thick of the details as to why I was doing this as
I’m saving that for another Slate specific post. I did realize that it had actually been a while since I had written a recursive function, and thought it would make for a good blog topic.
Turns out the only times I’ve written about recursion, it was in the context of following a directory structure and was limited to shell scripting and not in any of the web languages that I sling code in.
For those that may not be away, a recursive function is a function that calls upon itself. If you’re not careful, a poorly written self-referential function like this can go on forever and create an infinite loop.
Sample data set in TypeScript
An easy example of a recursive function would be something that takes a nested array of objects like I mentioned above, and perhaps tallies up some values to get a grand total.
Such an value would would look something like this:
interface NestedObject {
name: string;
value: number;
children?: NestedObject[];
}
const nestedObjects: NestedObject[] = [
{
name: 'First Parent',
value: 100,
children: [
{
name: 'First Child',
value: 110,
},
{
name: 'Second Child',
value: 120,
children: [
{
name: 'First Grandchild',
value: 121,
},
{
name: 'Second Grandchild',
value: 122,
},
],
},
],
},
{
name: 'Second Parent',
value: 200,
children: [
{
name: 'First Child',
value: 210,
},
{
name: 'Second Child',
value: 220,
},
{
name: 'Third Child',
value: 230,
children: [
{
name: 'First Grandchild',
value: 231,
},
],
},
],
},
];
TypeScriptFor the sake of example, I only went three levels deep, but you could nest things as far as you’d like or need to.
Then, we need to write a function that will take our nested array of objects as a parameter, loop through it, and if there are any children, repeat the process.
For example, I opted to use reduce
as it provides an accumulator variable that we can keep adding values to:
const totalValues = (nestedObjects: NestedObject[]) => {
return nestedObjects.reduce(
(totalValue, nestedObject: NestedObject): number => {
// Add the current object's value
totalValue += nestedObject.value;
// If we have children, let's add their values too
if (nestedObject.children) {
totalValue += totalValues(nestedObject.children);
}
// Return the new total
return totalValue;
},
0,
);
};
TypeScriptLastly, we can call our method and dump out some information to the console. I also wrote a poor man’s unit test to ensure we received the value we had expected to:
const totalValue = totalValues(nestedObjects);
console.log('Total value:', totalValue);
console.log('Value is correct:', totalValue === 1664);
TypeScriptNot much to it, and extremely powerful when dealing with data that could come in with varying levels of nesting!
Obviously, you could use this same code in vanilla JavaScript if you omit the
types 🙂