Reputation: 2543
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
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
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
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
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