如何在对象数组中填充缺失的键?

4
我有一个对象数组,它们应该都有相同的键,但某些键缺失。我想用通用值填充缺失的键。
我正在寻找一种简单的方法来实现这个目标(本地或通过库),我现在使用的代码可以工作,但是对于我这个不熟练的人来说看起来非常复杂,我确信我重新发明了一种繁琐的方法来做事情,而实际上有一个简单的方法。

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)


请注意,存在值为“undefined”的属性和根本不存在的属性之间存在区别。您的代码将它们视为相同的东西。 - T.J. Crowder
你真的事先不知道这些键吗? - T.J. Crowder
1
你在这种情况下的方法看起来不错,但是你可以考虑使用集合(set)而不是数组来存储“allkeys”。在你的代码中,“allkeys”的值最终变成了["a", "b", "c", "a", "c", "b", "c"],但实际上应该是["a", "b", "c"] - Cristian Lupascu
@T.J.Crowder:不,我从一个没有帮助的API中获取数据,所以我只会遇到不存在的键(而不是值为“undefined”的键)。 - WoJ
@GolfWolf:谢谢 - 我来自Python的世界,在那里我会使用精确地那个(一个集合),很高兴看到JS中也有相同的东西。 - WoJ
显示剩余2条评论
3个回答

6
这里有一种使用对象字面量中的属性扩展的版本,尽管这种方法在浏览器中的支持非常有限: 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);


@T.J.Crowder 谢谢,我之前不知道。我已经更新了我的回答。 - user184994
这也不是对象解构(它在ES2015中已经存在了)。它是“属性扩展语法”。 - T.J. Crowder
我想指出的是,根据MDN页面的说法,现在“所有”浏览器似乎都支持它(当然除了IE)。不过,我不确定你最初回答这个问题时的确切情况。 - Sahbi

2
您可以使用Object.assign将每个元素与一个包含默认键值的对象合并:

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


1
这需要提前知道键。您可以运行一个额外的步骤,从所有对象中存在的键构建defaultObj - Cristian Lupascu
OP 不事先知道密钥。 - T.J. Crowder
@Faly,你能否提供一个解决方案,使我可以将缺失的键的值设置为前一条记录的值,而不是全部设置为0? - Tanvi Agarwal

2

你的版本没问题,虽然我可能会避免所有那些数组concat调用,而是通过构建一个对象(或Set)和键一起构建。使用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;
}

关于*** 我会更改这个:请注意,一个存在但值为undefined的属性和一个根本不存在的属性是有区别的。你的代码将它们视为相同的东西。当然,如果你知道它们不会有undefined的值(例如,因为它们来自API)...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接