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\$
9
  • \$\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
    Commented Oct 17, 2019 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\$ Commented Oct 17, 2019 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
    Commented Oct 17, 2019 at 16:57
  • \$\begingroup\$ Or use Object.keys(obj).map() instead of checks. \$\endgroup\$
    – Rager
    Commented Oct 17, 2019 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
    Commented Oct 17, 2019 at 17:37

1 Answer 1

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\$
1
  • \$\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\$ Commented Oct 21, 2019 at 13:11

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.