2
\$\begingroup\$

So I have a loop that works great, but doing some testing I notice it is a bit slow. I get an average time within the loop of about 0.11 seconds. Include that with some arrays containing a length of over 100 and the time starts making a big difference. So I mainly want to improve the speed of the loop without compromising the functionality it already has (which is taking a JSON object/array and turning it into an html format). Below is the code along with the function that is called within the loop.

let replacements = '';
let ind = 0;
for (let w in obj[key]) {
    if (Array.isArray(obj[key])) {
        if (Array.isArray(obj[key][w])) {
            if (obj[key].length > 10) {
                replacements += "<font color='green'>Array(" + obj[key].length + ")</font>"
                break
            }
            replacements += ((ind == 0) ? "<font color='green'>Array(" : ", <font color='green'>Array(") + obj[key][w].length + ")</font>";
        } else {
            if (typeof obj[key][w] === 'object') {
                replacements += ((ind == 0) ? "" : ", ") + "{...}";
            } else {
                replacements += ((ind == 0) ? "" : ", ") + ObjectString(obj[key][w], "preview");
            }
        }
    } else {
        if (Array.isArray(obj[key][w]) || typeof obj[key][w] === 'object') {
            replacements += ((ind == 0) ? "" : ", ") + w + ": " + ((Array.isArray(obj[key][w])) ? "[...]" : "{...}");
        } else {
            replacements += ((ind == 0) ? "" : ", ") + w + ": ";
            replacements += ObjectString(obj[key][w], "preview");
        }
    }
    ind++;
}

function ObjectString(obj, type) { //~0.001-0.003 seconds
    let objString = obj;
    if (typeof objString == "string" && !objString.match(/<.*?>/g))
        objString = "<font color='red'>\"" + objString + "\"</font>";
    else if (/<.*?>/g.test(objString))
        objString = ((type == "normal") ? "<pre>" + process(objString).replace(/</g, '&lt;').replace(/>/g, '&gt;') + "</pre>" + objString : "<font color='red'>\"...\"</font>");
    else if (typeof objString == "number" || typeof objString == "boolean")
        objString = "<font color=' #947cf6'>" + objString + "</font>";

    return objString
}


function process(str) {//~0.001 seconds

    var div = document.createElement('div');
    div.innerHTML = str.trim();

    return format(div, 0).innerHTML;
}

Hopefully this can be optimized to increase its speed, but if it can't then I would at least like some help to clean it up as I am sure this isn't the most effective way to do it. Thanks!

Edit: added the process function that was previous missing. Also for clarification I am using regular JSON arrays and objects that usually exceed 100 objects in the array. If you want an example of what that data could look like https://en.wikipedia.org/wiki/GeoJSON#Example has a good structure of some of the data I am parsing (containing both objects and arrays).

\$\endgroup\$
  • \$\begingroup\$ What methods have you tried? I could be wrong, but for... in I think is slower than other methods, like a straight for(let x = 0; x < length; x++) method. To clean this up, you may want to look at a simple template engine like mustache. That would remove your html and probably do most of the heavy lifting. \$\endgroup\$ – Rager Oct 17 at 16:18
  • \$\begingroup\$ You are right with the for...in statement, they are generally slower. I tested moving it to a standard loop, but it seems like the major break is the check if its an array or object. \$\endgroup\$ – Manny K SoSo Oct 17 at 16:39
  • \$\begingroup\$ I think you're right. Is it a possibility to cast everything as desired type instead of checking? Or use the old JSON.parse(JSON.strigify(obj)) as a cast method? \$\endgroup\$ – Rager Oct 17 at 16:57
  • \$\begingroup\$ Or use Object.keys(obj).map() instead of checks. \$\endgroup\$ – Rager Oct 17 at 16:59
  • 1
    \$\begingroup\$ isArray and typeof can execute millions of times in 110ms and not the cause of the slowdown. It is the DOM as you create a div and then add HTML to it in process. That will be very slow, also the function format what ever it does? could make it even worse \$\endgroup\$ – Blindman67 Oct 17 at 17:37
2
\$\begingroup\$
  • Continuous repetitive string concatenation is bad for performance because each such operation requires re-hashing of the string due to String interning.
  • Array enumeration using for-in loop is slower than for-of or a standard for loop.
  • Things like obj[key][subkey] may be slow in a long loop so cache them in a variable.
  • Do the proper performance measurements using Devtools Timeline profiler to find bottlenecks.

Here's an arguably more readable and hopefully faster example:

const parts = [];
const group = obj[key];

if (Array.isArray(group)) {
  for (const val of group) {
    if (parts.length) parts.push(', ');
    if (Array.isArray(val)) {
      const tooLong = group.length > 10;
      const len = (tooLong ? group : val).length;
      parts.push(`<font color="green">Array(${len})</font>`);
      if (tooLong) break;
    } else {
      parts.push(objectPreview(val));
    }
  }
} else {
  for (const [key, val] of Object.entries(group)) {
    parts.push(`${parts.length ? ', ' : ''}${key}: ${
      Array.isArray(val) ? '[...]' : objectPreview(val)
    }`);
  }
}
const replacements = parts.join('');
function objectPreview(obj) {
  let str = obj;
  switch (typeof obj) {
    case 'object':
      str = '{...}';
      break;
    case 'string':
      if (/<.*?>/.test(obj)) {
        str = type === 'normal' ?
          `<pre>${process(obj).replace(/</g, '&lt;').replace(/>/g, '&gt;')}</pre>${obj}` :
          '<font color="red">"..."</font>';
      } else {
        str = `<font color="red">"${obj}"</font>`;
      }
      break;
    case 'number':
    case 'boolean':
      str = `<font color="#947cf6">${obj}</font>`;
      break;
  }
  return str;
}
\$\endgroup\$
  • \$\begingroup\$ I didn't realize that calling things like obj[key][subkey] caused slowdown's like it has. I will keep this in mind in the future. I also didn't think about using Template literals inside my code would clean it up that much, I will also keep this in mind for the future. And the switch case for readability was also something I didn't think of (since its easier to read and maintain in this form). With testing performance of 400+ entries (the most I encounter) I see no slow down. Thanks! \$\endgroup\$ – Manny K SoSo Oct 21 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.