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, '<').replace(/>/g, '>') + "</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).
1 Answer 1
- 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 thanfor-of
or a standardfor
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, '<').replace(/>/g, '>')}</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;
}
-
\$\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– Manny K SoSo2019年10月21日 13:11:42 +00:00Commented Oct 21, 2019 at 13:11
for... in
I think is slower than other methods, like a straightfor(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\$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\$JSON.parse(JSON.strigify(obj))
as a cast method? \$\endgroup\$Object.keys(obj).map()
instead of checks. \$\endgroup\$isArray
andtypeof
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 inprocess
. That will be very slow, also the functionformat
what ever it does? could make it even worse \$\endgroup\$