2
\$\begingroup\$

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.

asked Aug 10, 2017 at 16:17
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

There's two things that might help performance, and could also make your code easier to understand:

  1. only extract the length of the arrays once per loop, instead of per iteration

  2. 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.

answered Aug 10, 2017 at 16:36
\$\endgroup\$
2
  • \$\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\$ Commented 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\$ Commented Aug 11, 2017 at 8:40
1
\$\begingroup\$

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.

answered Aug 11, 2017 at 6:28
\$\endgroup\$
0

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.