# How to use recursion to turn object into string?

EDIT

To me JSON.stringify is not the solution here, as it is much slower than recursion

    JSON.stringify(fakeData)
.replace(/,/g, ';')
.replace(/:(?={)/g, '')
.replace(/"/g, '')

//Result
Recursion with reduce x 816,321 ops/sec ±7.38% (80 runs sampled)
JSON.stringify x 578,221 ops/sec ±1.72% (92 runs sampled)
Fastest is Recursion with reduce


I am making a css-in-js library, and I need to use recursion to turn object into string like this:

Input:

const fakeData = {
a: {
b: 'c',
d: 'e'
}
}



Output:

a{b:c;d:e;}


My recursion function:

const buildKeyframe = (obj) => {
return Object.entries(obj).reduce((acc, [prop, value]) => {
if (typeof value === 'string') {
return ${acc}${prop}:${value}; } return ${acc}${prop}:{${buildKeyframe(value)}}
}, '')
}


This function works, but I think there is room for improvement(e.g. use TCO, avoid using reduce)... How can I write a better function to recurse this data structure?

• The function does not work. Eg for object {a:{b:"1",c:{d:"2"}}} the pair b:"1" is missing from the returned string. – Blindman67 Jan 13 at 4:37
• Fixed the function now – John Winston Jan 13 at 4:47

I agree that JSON.stringify isn't the most appropriate for this problem, not just because it's slower, but also because it's a bit harder to understand. And you're also right in assuming that reduce() isn't the best tool for the job. Using a .map() reads in a much more natural way.

const convert = obj => (
Object.entries(obj)
.map(([key, value]) => (
typeof value === 'string'
? ${key}:${value};
: ${key}{${convert(value)}}
))
.join('')
)

console.log(convert({
a: {
b: 'c',
d: 'e'
},
}))

When optimizing this, think about the use case. While tail-call recursion may or may not be possible, it will make the code much more difficult to read, and it really won't help. The main advantage to tail-call recursion is to save memory on the call stack, and to let a recursive function call into itself an unlimited amount of times while still using just one call-frame worth of memory on the stack. There's no practical chunk of extremely nested CSS that can be written to make tail-call optimization worth it.

In your question, speed seems to be one of your primary concerns, but I'll play a bit of devil's advocate here and ask if it really should be for this use case. How much CSS-in-JS would you need to write before any of these micro-performance improvements even become noticeable? Tens of thousands of lines? Hundreds of thousands? I would suggest just building out the library first, run some benchmarks afterwards, and find out what parts of it actually run slow. Then only worry about fixing the slower areas.