4

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)

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. Commented Dec 18, 2017 at 14:49
  • You really don't know the keys in advance? 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 of allkeys in your code ends up being [ "a", "b", "c", "a", "c", "b", "c" ], but should really be ["a", "b", "c"]. 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) Commented Dec 18, 2017 at 14:53
  • @GolfWolf: thanks - I come from the Python world where I would have used exactlky that (a set), glad to see that there is the same in JS Commented Dec 18, 2017 at 14:54

3 Answers 3

6

Here is a version using property spread in object literals, although this will have very limited browser support:

https://developer.mozilla.org/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);

Sign up to request clarification or add additional context in comments.

3 Comments

@T.J.Crowder Thanks, I was unaware. I've updated my answer.
:-) It's also not object destructuring (which was in ES2015). It's "property spread syntax."
Just wanna point out that by now "all" browsers seem to support it, according to the MDN page (except IE of course). I'm not sure what the exact status was when you originally answered this question though.
2

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

Comments

2

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);

3 Comments

That requires knowing the keys in advance. You might run an additional step that builds the defaultObj from the keys present in all the objects.
The OP doesn't know the keys in advance.
@Faly , can you suggest a solution where I wish to add the value as the previous record value for missing keys instead of setting all as 0?

Your Answer

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.