I have JSON data from a Google sheet in a 3D array like this:
const values = [
[
// useless first array
],
[
'timestamp_dont_want',
'bird',
'',
'',
'easy',
'foot'
],
[
'timestamp_dont_want',
'bee',
'celery'
],
// lots of other arrays
[
'timestamp_dont_want',
'bat',
'chocolate',
'droid',
'',
'finger',
'',
'hella cool',
'indiana'
]
];
The maximum length of any given array is 12. Other than the entire first array and index 0 of every following array, each value (that is present and not empty) in an array corresponds to a desired object key label I have stored as 11 strings in another array:
const labels = [
'label_b',
'label_c',
'label_d',
'label_e',
'label_f',
'label_g',
'label_h',
'label_i',
'label_j',
'label_k',
'label_l'
];
Some arrays have all the values I need and many have empty values or stop at a certain index. I would like to create a new array of objects, containing only the useful and present data, that would be organized like this:
const organized = [
{
label_b: 'bird',
label_e: 'easy',
label_f: 'foot'
},
{
label_b: 'bee',
label_e: 'celery'
},
// lots of other objects
{
label_b: 'bat',
label_c: 'chocolate',
label_d: 'droid',
label_f: 'finger',
label_h: 'hella cool',
label_i: 'indigo'
}
];
I wrote a function, but it doesn't feel very elegant:
function sort (arr3d, arrKeys) { // Passing values and labels constants from above
let organized = []; // Initiate array
for (var i = 1; i < arr3d.length; i++) { // For each array of values, starts at 1 to ignore first useless array
organized.push({}); // Create empty objects
}
for (var j = 0; j < arrKeys.length; j++) { // Outer loop once for each unique object key
for (var k = 1; k < arr3d.length; k++) { // Inner loop for each array of values, starts at 1 to ignore first useless array
if (arr3d[k][j + 1]) { // Testing presence of value before writing in next step, ignoring index 0 of each array
organized[k - 1][arrKeys[j]] = arr3d[k][j + 1].trim(); // Craete new key in object; trim value from associated array and assign to key
}
}
}
console.log(organized); // Prove it worked
}
Any ideas about how to refactor or optimize? Maybe you can teach me about using a new JS method.
2 Answers 2
There's two things that might help performance, and could also make your code easier to understand:
only extract the length of the arrays once per loop, instead of per iteration
deference deeply nested structures earlier, and as infrequently as possible
For #1, I normally use:
for (var i = 0, n = array.length; i < n; ++i) { ... }
For #2, look at your innermost nested line:
organized[k - 1][arrKeys[j]] = arr3d[k][j + 1].trim();
Consider reversing the order in which you enumerate over the data. There's more scope for optimising values from the inner loop to the outer loop if you iterate over the "leftmost" components of your 2D structures first:
for (var k = 1, kn = arr3d.length; k < kn; k++) {
var o = organized[k - 1] = {};
var a = arr3d[k];
for (var j = 0, jn = arrKeys.length; j < jn; j++) {
var t = a[j + 1];
if (t) {
o[arrKeys[j]] = t.trim();
}
}
}
Note also that now that the loops are the other way around there's no need to pre-create the objects that make up the organized
array - it's initialised with an empty object in the first line of the outer loop.
-
\$\begingroup\$ Thanks for responding. I get no. 1: don't repeat a static operation when it can be assigned to memory. I am going to have to come back to no. 2 a bit later because my brain is kind of fried at the moment. \$\endgroup\$jsejcksn– jsejcksn2017年08月10日 17:04:14 +00:00Commented Aug 10, 2017 at 17:04
-
\$\begingroup\$ the main point of #2 is that in your original code you had seven property accesses per inner loop pass. My revised version has three (albeit there's now also two in the outer loop, but those happen fewer times than they did before, too). For a 10x10 loop you've gone from 700 operations down to 320. \$\endgroup\$Alnitak– Alnitak2017年08月11日 08:40:35 +00:00Commented Aug 11, 2017 at 8:40
You should split your function into two:
function convertObject(arr, labels) {
const obj = {};
for (let i = 1; i < arr.length; i++) {
if (arr[i] !== '') { // changed to type-safe comparison operator
obj[labels[i]] = arr[i].trim();
}
}
return obj;
}
function convertArray(arr, labels) {
const result = [];
for (let i = 1; i < arr.length; i++) {
result.push(convertObject(arr[i], labels));
}
return result;
}
I changed the function name to convert
since this has nothing to do with sorting.
Also, your arr3d
was not 3-dimensional at all, therefore I changed its name, too.