T3STY T3STY - 1 month ago 7
Javascript Question

optimize array building from nested arrays of objects

I want to optimize the extraction of a property's value from an array of objects, each object containing other nested arrays of objects, in Javascript.

I'm not really sure how to explain it by words, so here is some code to explain what I'm trying to optimize:

// test case
var first = [
{ second: [ { id: 1}, { id: 2}, { id: 3} ] },
{ second: [ { id: 4}, { id: 5}, { id: 6} ] },
{ second: [ { id: 7}, { id: 8}, { id: 9} ] },
{ second: [ { id: 10}, { id: 11}, { id: 12} ] }
];

// where the id values will be stored
var arrIDs = [];

// extracting the id values
for (var i=0; i<first.length; i++){
for (var j=0; j<first[j].second.length; j++){
// I want to avoid all these push() calls
arrIDs.push(first[j].second[j].id);
}
}


And this is the end result that I want to achieve:

arrIDs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];


I may need that with up to 3 or 4 nested levels, but I may use a different structure to reduce it to 2 levels if it's not possible to optimize more than that.

What I would like to optimize the most is actually all those Array().push() calls inside the for loops.

Does anyone know a good way of doing this?

EDIT

I have forgot to mention that I would need to use this in an environment where IE8 is the best option we have. So ES6 is not an option.

Answer

You could use this recursive ES6 function. It works for any level, and you can pass it an array, plain object, or anything else (in the latter case you get an empty array as result):

function getAll(obj, key) {
    return obj !== Object(obj) ? [] 
        : Object.keys(obj).reduce ( (acc, k) => 
            acc.concat(k == key ? obj[k] : getAll(obj[k], key)), [] ); 
}

// sample data with different levels and mix of array / object alterations
var first = [
  { a: 2, id:  1}, { second: [ { id:  2}, { id:  3} ] },
  { second: [ { id:  4}, { id:  5}, { id:  6} ] },
  { second: [ { id:  7}, { third: {id:  6}}, { id:  9} ] },
  { second: [ { id: 10}, { id: 11}, { id: 12} ] }
];

var result = getAll(first, 'id');

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

For IE8 compatibility:

function getAll(obj, key) {
    if (typeof obj != 'object' || obj == null) return [];
    var acc = [];
    for (var k in obj) {
        acc = acc.concat( k == key ? obj[k] : getAll(obj[k], key) );
    }
    return acc; 
}

// sample data with different levels and mix of array / object alterations
var first = [
  { a: 2, id:  1}, { second: [ { id:  2}, { id:  3} ] },
  { second: [ { id:  4}, { id:  5}, { id:  6} ] },
  { second: [ { id:  7}, { third: {id:  6}}, { id:  9} ] },
  { second: [ { id: 10}, { id: 11}, { id: 12} ] }
];

var result = getAll(first, 'id');

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments