Chapo
Chapo

Reputation: 2543

Efficiently fill values in an array of objects in JavaScript

I have an array of objects like :

a = [{"a":1,"b":2},{"a":3,"c":5}]

I would like to obtain, in an efficient way :

b = [{"a":1,"b":2,"c":"blabla"},{"a":3,"b":"blabla","c":5}]

My function so far is (using the underscoreJS library in the first line):

let fillFuncArr = (res,fill) => {
    // we fill the potential missing values
    let col_names = _.uniq(res.map(x => Object.keys(x)).reduce((acc, curr) => acc.concat(curr), []), false);
    for (let i = 0; i < res.length; i++) {
        for (let j = 0; j < col_names.length; j++) {
            if (!res[i].hasOwnProperty(col_names[j])) {
                res[i][col_names[j]] = fill;
            }
        }
    }
    return res;
};

In my example above you would do :

b = fillFuncArr(a,"blabla")

How to make that function faster if at all possible ?

===========================================================================

EDIT after answers to benchmark replies :

I tested the functions like this :

for (let i=0;i<num_tries;i++) {
    let time_tmp = performance.now();
    console.time("old");
    fillFuncArr(obj,"blah");
    console.timeEnd("old");
    time_old.push(performance.now()-time_tmp);
    time_tmp = performance.now();
    console.time("new");
    fillFuncArrNew(obj,"blah");
    console.timeEnd("new");
    time_new.push(performance.now()-time_tmp);
}

This answer (the first calculation of the old function is always much faster than the subsequent ones, not quite sure why...) is 50-100 times faster. The fill time is the same, it's getting the keys that makes up all the speed gains :

 "old": [
        147.52006196975708,
        1065.4309248924255,
        1023.5124139785767,
        1021.830512046814,
        1855.5670911073685,
        1006.7114781141281,
        996.8541929721832,
        1306.3085260391235
    ],
    "new": [
        18.814231991767883,
        23.46549105644226,
        17.708116054534912,
        15.55942702293396,
        18.764864921569824,
        15.866382002830505,
        19.18179702758789,
        23.987511038780212
    ]

Upvotes: 0

Views: 142

Answers (4)

Vincent
Vincent

Reputation: 4753

const a = [{"a":1,"b":2},{"a":3,"c":5}];
const allKeys=[...new Set(a.flatMap(Object.keys))]
const dummyObj=Object.fromEntries(allKeys.map(key => [key, 'blabla']));
console.log(a.map(data => ({...dummyObj, ...data})))

Upvotes: 0

StackSlave
StackSlave

Reputation: 10627

Take a look here:

console.time('allTest');
var was = [{a:1, b:2}, {a:3, c:5}];
function blah(array){
  var a = [], o, b = 'blabla';
  array.forEach(function(w){
    o = {};
    o.a = 'a' in w ? w.a : b;
    o.b = 'b' in w ? w.b : b;
    o.c = 'c' in w ? w.c : b;
    a.push(o);
  });
  return a;
}
console.time('test'); console.log(blah(was)); console.timeEnd('test'); console.timeEnd('allTest');

At least this will show you how to test the time.

Upvotes: 0

georg
georg

Reputation: 214949

Don't know if this is faster, but definitely shorter:

dummy = {a: 'dummy', b: 'dummy', c: 'dummy'}

a = [{"a": 1, "b": 2}, {"a": 3, "c": 5}]

r = a.map(x => ({...dummy, ...x}))

console.log(r)

If you want the dummy to be fully dynamic, then

function fillKeys(a, value) {
    let keys = new Set(a.flatMap(Object.keys)),
        dummy = Object.fromEntries(
            [...keys].map(k => [k, value]));
    return a.map(x => ({...dummy, ...x}));
}

//

a = [{"a": 1, "b": 2}, {"a": 3, "c": 5}]

r = fillKeys(a, 'dummy')

console.log(r)

That being said, I'm sure this is actually an XY problem, so it would help to explain us what you're actually doing. For example, if you just want all objects in the list to respond to the same set of keys, that would be much easier (and also faster) with proxies.

Upvotes: 2

CertainPerformance
CertainPerformance

Reputation: 370679

First figure out all keys in the array items, create a "default" object filled with the fill as its values, then iterate over the items and spread the default object, then the original object:

const fillFuncArr = (arr, fill) => {
  const allKeys = new Set(arr.flatMap(Object.keys));
  const defaultObj = {};
  for (const key of allKeys) {
    defaultObj[key] = fill;
  }
  return arr.map(obj => ({ ...defaultObj, ...obj }));
};

a = [{"a":1,"b":2},{"a":3,"c":5}]
b = fillFuncArr(a,"blabla")
console.log(b);

Upvotes: 1

Related Questions