使用 JavaScript reduce 来更新和添加到对象数组

Using JavaScript reduce to update and add to array of objects

我正在尝试解决 JavaScript 中的一个问题,并且相信使用 reduce 方法会是最优雅的。我有一组看起来像这样的对象:

let existingLots = [
    {
        id: 1,
        title: 'one',
        colour: 'red',
        speed: 100
    },
    {
        id: 2,
        title: 'two',
        colour: 'blue',
        speed: 70
    },
    {
        id: 3,
        title: 'three',
        colour: 'white',
        speed: 120
    },
    {
        id: 4,
        title: 'four',
        colour: 'yellow',
        speed: 90
    },
]

另一个数组如下所示:

let newLots = [
    {
        id: 1,
        title: 'one',
        colour: 'orange',
        speed: 100,
        owner: 'Ted'
    },
    {
        id: 2,
        title: 'two',
        colour: 'blue',
        speed: 75
    },
    {
        id: 3,
        title: 'three updated',
        colour: 'white',
        speed: 120,
    },
    {
        id: 4,
        title: 'four',
        colour: 'yellow',
        speed: 90
    },
    {
        id: 5,
        title: 'five',
        colour: 'green',
        speed: 50
    },
]

如您所见,某些 ID 之间存在重叠。我想更新 existingLots 数组,这样如果一个对象与 newLots 数组中的对象共享相同的 id,则该对象会更新为任何新的 properties/values在它的 newLots 对应物中。另外,我希望 newLots 中的所有对象(即 5id 的对象)不在 existingObjects 中的对象添加到 existingObjects数组。

我尝试了以下但没有达到预期的结果:

const res = existingLots.reduce((acc, obj) => {
    let updatedObj = newLots.filter(newLot => newLot.id === obj.id);
    // if object exists in currentLots, merge it
    if(updatedObj) {
        [...acc, {...obj, ...updatedObj}]
    } else {
        let newObj = !newLots.filter(newLot => newLot.id === obj.id);
        [...acc, {...newObj}]
    }
    return acc
}, [])

我是 JavaScript 数组方法的新手,非常感谢您的帮助。

1) 您正在使用 filter 这将 return 一个过滤器元素数组

The filter() method creates a new array with all elements that pass the test implemented by the provided function. - MDN

2) 此代码只是 push/add 数组中最后一个元素之后的所有对象,然后被丢弃,因为您没有将它们存储在任何地方

[...acc, {...obj, ...updatedObj}]

解决方案: 您可以使用 forEach 遍历 newLots 数组并将数据添加到 existingLots 元素

newLots.forEach((newLot) => {
  let objExist = existingLots.find((o) => o.id === newLot.id);
  if (objExist) {
    for (let key in newLot) objExist[key] = newLot[key];
  } else existingLots.push(newLot);
});

newLots.forEach((newLot) => {
  let index = existingLots.findIndex((o) => o.id === newLot.id);
  if (index !== -1) existingLots[index] = { ...existingLots[index], ...newLot };
  else existingLots.push(newLot);
});

let existingLots = [
  {
    id: 1,
    title: "one",
    colour: "red",
    speed: 100,
  },
  {
    id: 2,
    title: "two",
    colour: "blue",
    speed: 70,
  },
  {
    id: 3,
    title: "three",
    colour: "white",
    speed: 120,
  },
  {
    id: 4,
    title: "four",
    colour: "yellow",
    speed: 90,
  },
];

let newLots = [
  {
    id: 1,
    title: "one",
    colour: "orange",
    speed: 100,
    owner: "Ted",
  },
  {
    id: 2,
    title: "two",
    colour: "blue",
    speed: 75,
  },
  {
    id: 3,
    title: "three updated",
    colour: "white",
    speed: 120,
  },
  {
    id: 4,
    title: "four",
    colour: "yellow",
    speed: 90,
  },
  {
    id: 5,
    title: "five",
    colour: "green",
    speed: 50,
  },
];

newLots.forEach((newLot) => {
  let index = existingLots.findIndex((o) => o.id === newLot.id);
  if (index !== -1) existingLots[index] = { ...existingLots[index], ...newLot };
  else existingLots.push(newLot);
});

console.log(existingLots);

我认为您唯一可以使用一些“魔法”的地方是查找。此代码在对象 id-s 和它们在实际数组中的索引之间创建一个 map,然后遍历更新程序数组,使用 map 查找和更新现有对象,如果id 是全新的。

let existingLots = [{
    id: 1,
    title: 'one',
    colour: 'red',
    speed: 100
  },
  {
    id: 2,
    title: 'two',
    colour: 'blue',
    speed: 70
  },
  {
    id: 3,
    title: 'three',
    colour: 'white',
    speed: 120
  },
  {
    id: 4,
    title: 'four',
    colour: 'yellow',
    speed: 90
  },
];

let newLots = [{
    id: 1,
    title: 'one',
    colour: 'orange',
    speed: 100,
    owner: 'Ted'
  },
  {
    id: 2,
    title: 'two',
    colour: 'blue',
    speed: 75
  },
  {
    id: 3,
    title: 'three updated',
    colour: 'white',
    speed: 120,
  },
  {
    id: 4,
    title: 'four',
    colour: 'yellow',
    speed: 90
  },
  {
    id: 5,
    title: 'five',
    colour: 'green',
    speed: 50
  },
];

let map = existingLots.reduce((acc, obj, idx) => acc.set(obj.id, idx), new Map());
for (let obj of newLots)
  if (map.has(obj.id)) {
    let idx = map.get(obj.id);
    existingLots[idx] = {...existingLots[idx], ...obj};
  } else
    existingLots.push(obj);


console.log(existingLots);

更新程序部分实际上也可以在您的代码中工作,就像 filter() 总是给您一个数组一样,if(updatedObj) 应该是 if(updatedObj.length)updatedObj 的用法将是 updatedObj[0]acc =(或简单的 return)也不见了。 但是所有这些数组方法(filter()reduce()map())通常都在单个数组上工作,它们并不真正适合根据条件从另一个数组追加元素,因此second if 只是无法工作 - 为此,你最好使用一个简单的循环,因为这两个答案都建议,然后更新可能会在同一个循环中发生。