Как изменить массив, который одно из свойств объекта распространяется на другое в javascript - PullRequest
2 голосов
/ 17 марта 2020

Я пытаюсь изменить массив, в котором одно из свойств объекта распространяется на другое. IE создает постоянный результат, где фруктовые продукты добавляются к овощным продуктам. Я могу изолировать типы фруктов и овощей в их собственном массиве с помощью .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'] } ]

Ответы [ 3 ]

2 голосов
/ 17 марта 2020

Об этих проблемах легче думать небольшими шагами, и мне нравится использовать функциональное программирование для обработки данных. Во-первых, давайте извлечем фрукты и овощи из остальных.

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

РЕДАКТИРОВАТЬ: Вот несколько пояснений, основанных на комментариях.

Q: Есть ли в стороне, чтобы сохранить свойство от первоначального veg obj в конечном результате без его жесткого кодирования?

Если вы не возражаете изменить исходную запись type="veg", вы можете вместо этого перезаписать ее свойство items только для включения в нее фруктов. Но этот вопрос звучит так, как будто вы знаете, что есть ровно одна овощная и одна фруктовая запись, и в этом случае приведенный выше код был сложен даром, и мы могли бы просто использовать это:

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.

1 голос
/ 17 марта 2020

Если я понял ваш вопрос, ваша задача не менять исходный массив как есть. В этом случае вам нужно клонировать начальный массив, а затем делать все, что вы хотите.

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);
1 голос
/ 17 марта 2020

Логика c может звучать очень замысловато, но на самом деле она довольно проста, когда вы ее ломаете. Что вы хотите сделать:

  1. Используйте Array.prototype.reduce() и начните с пустого массива. Пустой массив - это начальное состояние аккумулятора.
  2. Когда вы находитесь у текущего элемента (который является вложенным объектом):
    • Если это type, это не 'veg' или 'fruit', тогда вы просто кладете sh текущий элемент в аккумулятор
    • Если это так, то есть две возможности:
      • Запись { type: 'veg', items: [...] } еще не существует. Затем мы просто создаем новый и назначаем элементы для элементов текущей записи, то есть { type: 'veg', items: current.items
      • Запись на самом деле уже существует. Затем мы используем разброс массива для объединения items массива

См. Подтверждение концепции ниже:

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