Уменьшить массив объектов до уникальных ключей и добавить массив значений, общих для этого ключа

#javascript #ecmascript-6 #reduce #ecmascript-5

#javascript #ecmascript-6 #уменьшить #ecmascript-5

Вопрос:

Я хотел бы сгруппировать значения этого массива по его car ключу, а затем поместить значения, общие для этого car, в массив значений.

Мне удалось сделать это с помощью этого, но было интересно, есть ли более простой способ сделать это с помощью reduce .

 const arr = [{
  car: 'audi',
  value: 'black'
}, {
  car: 'audi',
  value: 'expensive'
}, {
  car: 'fiat',
  value: 'red'
}, {
  car: 'fiat',
  value: 'cheap'
}]

// Simple array with unique car
const cars = Array.from(new Set(arr.map(({ car }) => car)))
// Array of objects with unique `car` and an empty `values` array for each
const result = cars.map((car) => ({ car, values: [] }))

// Push to values array the `value` for each car
arr.map((obj) => {
  result.map((res) => {
    if (obj.car === res.car) {
      res.values.push(obj.value)
    }
  })
})

console.log(result)
/*
[{
  car: 'audi',
  values: ['black', 'expensive']
}, {
  car: 'fiat',
  values: ['red', 'cheap']
}]
*/
  

Ответ №1:

Создайте объект, проиндексированный по car имени, затем выполните итерацию по исходному массиву, перенося значение в массив объекта:

 const arr = [{
  car: 'audi',
  value: 'black'
}, {
  car: 'audi',
  value: 'expensive'
}, {
  car: 'fiat',
  value: 'red'
}, {
  car: 'fiat',
  value: 'cheap'
}];

const carsByName = {};
for (const { car, value } of arr) {
  if (!carsByName[car]) carsByName[car] = { car, value: [] };
  carsByName[car].value.push(value);
}
console.log(Object.values(carsByName));  

Хотя это можно было бы сделать reduce , это, возможно, не очень семантически уместно, когда накопитель никогда не меняется (и немного шумит синтаксически):

 const arr = [{
  car: 'audi',
  value: 'black'
}, {
  car: 'audi',
  value: 'expensive'
}, {
  car: 'fiat',
  value: 'red'
}, {
  car: 'fiat',
  value: 'cheap'
}];

const carsByName = arr.reduce((a, { car, value }) => {
  if (!a[car]) a[car] = { car, value: [] };
  a[car].value.push(value);
  return a;
}, {});
console.log(Object.values(carsByName));  

Комментарии:

1. Спасибо, выглядит отлично, могу ли я сделать [carsByName] вместо Object.values(carsByName)?

2. Это даст вам массив, содержащий один объект, причем этот объект индексируется по имени автомобиля. Если эта структура данных — это то, что вы хотите, пойдите для этого, но это не то, что вы ожидали получить в вопросе.

3. Вы правы, большое спасибо за то, что код отлично работает

Ответ №2:

Просто используйте reduce для этого. Сохранить в acc массиве автомобилей с ключами car и values . А затем сопоставить его. Я имею в виду:

 const arr = [
  {
    car: "audi",
    value: "black",
  },
  {
    car: "audi",
    value: "expensive",
  },
  {
    car: "fiat",
    value: "red",
  },
  {
    car: "fiat",
    value: "cheap",
  },
];

const result = Object.entries(
  arr.reduce((acc, { car, value }) => {
    if (acc[car]) {
      return {
        ...acc,
        [car]: [...acc[car], value],
      };
    }

    return { ...acc, [car]: [value] };
  }, [])
).map(([car, values]) => ({ car, values }));

console.log(result);  

Ответ №3:

Вы можете использовать .reduce для группировки элементов по car , а затем .map для создания списка объектов, имеющих car и его values :

 const arr = [{
  car: 'audi',
  value: 'black'
}, {
  car: 'audi',
  value: 'expensive'
}, {
  car: 'fiat',
  value: 'red'
}, {
  car: 'fiat',
  value: 'cheap'
}]

let result = arr.reduce((acc,item) => {
  const values = acc[item.car];
  acc[item.car] = values ? [...values, item.value] : [item.value];
  return acc;
}, {});

result = Object.entries(result).map(([car, values]) => ({car,values}));

console.log(result)