I have an Array of Objects which should all have the same keys, but some of the keys are missing. I would like to fill in the missing keys with a generic value.
I am looking for a simple way to do that (natively or via a library), the code below I use now works, bit looks to my untrained eyes quite heavy and I am sure I reinvented the tedious way to do something while there is a simple one.
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
]
// get the list of all keys
var allkeys = []
arr.forEach((objInArr) => {
allkeys = allkeys.concat(Object.keys(objInArr))
})
// check all arr entries for missing keys
arr.forEach((objInArr, i) => {
allkeys.forEach((key) => {
if (objInArr[key] === undefined) {
// the generic value, in this case 0
arr[i][key] = 0
}
})
})
console.log(arr)
I have an Array of Objects which should all have the same keys, but some of the keys are missing. I would like to fill in the missing keys with a generic value.
I am looking for a simple way to do that (natively or via a library), the code below I use now works, bit looks to my untrained eyes quite heavy and I am sure I reinvented the tedious way to do something while there is a simple one.
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
]
// get the list of all keys
var allkeys = []
arr.forEach((objInArr) => {
allkeys = allkeys.concat(Object.keys(objInArr))
})
// check all arr entries for missing keys
arr.forEach((objInArr, i) => {
allkeys.forEach((key) => {
if (objInArr[key] === undefined) {
// the generic value, in this case 0
arr[i][key] = 0
}
})
})
console.log(arr)
Share
Improve this question
asked Dec 18, 2017 at 14:47
WoJWoJ
30.1k58 gold badges214 silver badges405 bronze badges
7
-
Note that there's a difference between a property that exists and has the value
undefined
and a property that doesn't exist at all. Your code is treating them as the same thing. – T.J. Crowder Commented Dec 18, 2017 at 14:49 - You really don't know the keys in advance? – T.J. Crowder Commented Dec 18, 2017 at 14:49
-
1
Your approach in this case seems OK, but you might consider using a set instead of an array for
allkeys
. The value ofallkeys
in your code ends up being[ "a", "b", "c", "a", "c", "b", "c" ]
, but should really be["a", "b", "c"]
. – Cristian Lupascu Commented Dec 18, 2017 at 14:51 -
@T.J.Crowder: no, I get the data from an otherwise unhelpful API so I will have the case of non-existing keys only (not ones which have the value
undefined
) – WoJ Commented Dec 18, 2017 at 14:53 - @GolfWolf: thanks - I e from the Python world where I would have used exactlky that (a set), glad to see that there is the same in JS – WoJ Commented Dec 18, 2017 at 14:54
3 Answers
Reset to default 6Here is a version using property spread in object literals, although this will have very limited browser support:
https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
]
// Create an object with all the keys in it
// This will return one object containing all keys the items
let obj = arr.reduce((res, item) => ({...res, ...item}));
// Get those keys as an array
let keys = Object.keys(obj);
// Create an object with all keys set to the default value (0)
let def = keys.reduce((result, key) => {
result[key] = 0
return result;
}, {});
// Use object destrucuring to replace all default values with the ones we have
let result = arr.map((item) => ({...def, ...item}));
// Log result
console.log(result);
Your version is fine, although I would probably avoid all those array concat
calls by just building up an object (or Set
) with the keys. It's also a bit less clunky with for-of
:
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
];
// Get all the keys
const keyObj = Object.create(null);
for (const entry of arr) {
for (const key of Object.keys(entry)) {
keyObj[key] = true;
}
}
const allkeys = Object.keys(keyObj);
// Check all arr entries for missing keys
for (const entry of arr) {
for (const key of allkeys) {
if (entry[key] === undefined) { // ***I'd change this
entry[key] = 0;
}
}
}
console.log(arr);
.as-console-wrapper {
max-height: 100% !important;
}
Re *** I'd change this
: Note that there's a difference between a property that exists and has the value undefined
and a property that doesn't exist at all. Your code is treating them as the same thing. Of course, if you know they won't have the value undefined
(for instance, because of the API you're getting them from)...
You can use Object.assign to merge each element with an object holding default key-values:
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
];
var defaultObj = arr.reduce((m, o) => (Object.keys(o).forEach(key => m[key] = 0), m), {});
arr = arr.map(e => Object.assign({}, defaultObj, e));
console.log(arr);