0
\$\begingroup\$

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?

asked Jan 13, 2021 at 3:42
\$\endgroup\$
2
  • \$\begingroup\$ The function does not work. Eg for object {a:{b:"1",c:{d:"2"}}} the pair b:"1" is missing from the returned string. \$\endgroup\$ Commented Jan 13, 2021 at 4:37
  • \$\begingroup\$ Fixed the function now \$\endgroup\$ Commented Jan 13, 2021 at 4:47

1 Answer 1

1
\$\begingroup\$

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.

answered Jan 15, 2021 at 8:48
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.