如何在 javascript 中更改其中一个对象属性传播到另一个对象属性的数组

How to change array that one of the objects properties spread in to another in javascript

我正在尝试更改其中一个对象属性散布到另一个的数组。 I.E 创建常量结果,其中将水果项添加到蔬菜项中。我可以使用 .filter(item.type === 'veg' || item.type === 'fruit') 将水果和蔬菜类型隔离到它们自己的数组中但我不确定如何保持第一个数组的原样。我尝试使用 .reduce 这样做,但没有任何运气,因为我仍然需要数据数组的第一部分。

如有任何帮助,我们将不胜感激。

const data = [ { type: 'fish', weight: '2kg' }, { type: 'veg', items: ['carrot', 'cabbage', 'pea'] }, { type: 'fruit', items: ['apple', 'pear', 'orange' ]} ]
const result = [ { type: 'fish', weight: '2kg' }, { type: 'veg', items: ['carrot', 'cabbage', 'pea', 'apple', 'pear', 'orange'] } ]

逻辑听起来很复杂,但分解后其实很简单。你要做的是:

  1. 使用Array.prototype.reduce()并先从一个空白数组开始。空白数组是累加器的起始状态。
  2. 当您位于当前项目(这是一个嵌套对象)时:
    • 如果type不是'veg'或'fruit',那么你只需将当前项压入累加器
    • 如果是,那么有两种可能:
      • 条目 { type: 'veg', items: [...] } 尚不存在。然后,我们简单地创建一个新的,并将项目分配给它作为当前条目的项目,即 { type: 'veg', items: current.items
      • 条目实际上已经存在。然后,我们使用 array spread 合并 items array

请参阅下面的概念验证:

const data = [{
  type: 'fish',
  weight: '2kg'
}, {
  type: 'veg',
  items: ['carrot', 'cabbage', 'pea']
}, {
  type: 'fruit',
  items: ['apple', 'pear', 'orange']
}];

const result = data.reduce((acc, cur) => {
  // If type is not veg/fruit, we simply push it to the accumulator
  if (cur.type !== 'veg' && cur.type !== 'fruit') {
    acc.push(cur);
  }

  // Otherwise, we want to merge the items array
  else {
    // Find if the accumulator contains an object with type 'veg'
    const vegIndex = acc.findIndex(entry => entry.type === 'veg');
    
    // If it doesn't exist, then we create a new object
    if (vegIndex === -1) {
      acc.push({
        type: 'veg',
        items: cur.items
      });
    }
    
    // Otherwise, we simply spread the array and merge it
    else {
      acc[vegIndex].items = [...acc[vegIndex].items, ...cur.items];
    }
  }

  return acc;
}, []);

console.log(result);

如果我理解你的问题,你关心的是不要按原样更改起始数组。 在这种情况下,您必须克隆起始数组,然后执行您想要的所有操作。

const data = [ 
   { type: 'fish', weight: '2kg' },
   { type: 'veg', items: ['carrot', 'cabbage', 'pea'] },
   { type: 'fruit', items: ['apple', 'pear', 'orange'] }
];
const result = [...data];

 result.find(x => x.type === 'veg').items = result.filter(x => x.type === 'veg' || 
 x.type === 'fruit').flatMap(x => x.items);
 result.pop();

 console.log(data, result);

这些问题小步骤比较容易想到,我喜欢用函数式编程来处理数据。首先,让我们从剩下的水果和蔬菜中提取出来。

const produce = data.filter(entry => ["fruit", "veg"].includes(entry.type));
const remainder = data.filter(entry => !["fruit", "veg"].includes(entry.type));

从技术上讲,我们可以使用 forEach 同时构建两者,而不是两次调用过滤器,但我想说这里的清晰度胜过效率。

接下来我们可以将水果和蔬菜项目与 .reduce 函数合并,并巧妙地使用 ... 扩展符号。

const mergedItems = produce.reduce((acc, entry) => [...acc, ...entry.items], []);

最后,我们可以再次使用扩展符号将这些全部合并到一个新对象中:

const result = [
  { type: "veg", items: mergedItems },
  ...remainder
];

给你。结果如下:

const data = [
    { type: 'fish', weight: '2kg' },
    { type: 'veg', items: ['carrot', 'cabbage', 'pea'] },
    { type: 'fruit', items: ['apple', 'pear', 'orange' ]}
];
const produce = data.filter(entry => ["fruit", "veg"].includes(entry.type));
const remainder = data.filter(entry => !["fruit", "veg"].includes(entry.type));
const mergedItems = produce.reduce((acc, entry) => [...acc, ...entry.items], []);
const result = [
  { type: "veg", items: mergedItems },
  ...remainder
];

console.log(result);


编辑:这里有一些基于评论的澄清。

问:有没有办法在不硬编码的情况下在最终结果中保留一个 属性 来自原始蔬菜对象?

如果您不介意修改原来的 type="veg" 条目,您可以改写它的条目 属性,只包含水果。但是这个问题听起来好像你知道只有一个蔬菜和一个水果条目,在这种情况下,上面的代码毫无意义地复杂了,我们可以只使用这个:

const data = [
    { type: 'fish', weight: '2kg' },
    { type: 'veg', price: "3.00", items: ['carrot', 'cabbage', 'pea'] },
    { type: 'fruit', items: ['apple', 'pear', 'orange' ]}
];

const vegEntry = data.find(entry => entry.type === "veg");
const fruitIndex = data.findIndex(entry => entry.type === "fruit");

vegEntry.items = [...vegEntry.items, ...data[fruitIndex].items];
data.splice(fruitIndex, 1);

console.log(data);

这会将水果项目添加到蔬菜条目项目并删除水果条目,因此保留了蔬菜条目的所有属性。

如果出于某种原因您不想就地修改数据而宁愿创建一个新的结果对象,您可以将其更改为:

const data = [
    { type: 'fish', weight: '2kg' },
    { type: 'veg', price: "3.00", items: ['carrot', 'cabbage', 'pea'] },
    { type: 'fruit', items: ['apple', 'pear', 'orange' ]}
];

const vegIndex = data.findIndex(entry => entry.type === "veg");
const fruitIndex = data.findIndex(entry => entry.type === "fruit");

const newVegEntry = {...data[vegIndex]};
newVegEntry.items = [...newVegEntry.items, ...data[fruitIndex].items];

const result = [...data];
result[vegIndex] = newVegEntry;
result.splice(fruitIndex, 1);

console.log(result);

如果这在您的浏览器中不起作用,可能是因为 {...data[vegIndex]} 语法更现代一些。您可能想使用 Object.assign({}, data[vegIndex]} 来创建 veg 条目的浅表副本。