Группировать массив объектов на основе определенного поля

#javascript #arrays #group-by

#javascript #массивы #группировать по

Вопрос:

У меня есть массив объектов. Я хочу сгруппировать их по определенному полю.

 [
    {
        "name": "JOHN",
        "type": 1,
        "sum": 5
    },
    {
        "name": "SERA",
        "type": 1,
        "sum": 43
    },
    {
        "name": "SERA",
        "type": 2,
        "sum": 129
    },
    {
        "name": "JOHN",
        "type": 2,
        "sum": 200
    }
]
 

Результат, который я ожидаю для группировки по имени атрибута, выглядит следующим образом.

 {
    // Group #1

    "JOHN": [
      {
        "type": 2,
        "sum": 200
      }
      {
        "type": 1,
        "sum": 5
      }
    ],

    // Group #2

    "SERA":[
     {
        "type": 1,
        "sum": 43
     },
     {
        "type": 2,
        "sum": 129
     },
    ]
}
 

Я использовал вложенные циклы, но, к сожалению, скорость выполнения была низкой, и это не давало правильных результатов.

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

1. может ли вывод также быть полностью заключен в { } квадратные скобки? таким образом, полный результат — это объект, а не массив? Я думаю, в противном случае это невозможно

2. @Saeed Вывод синтаксически некорректен

3. @caramba Да, он может быть полностью заключен в { } квадратные скобки.

Ответ №1:

Как вы уже упоминали, мы можем использовать объект вместо массива для самой внешней оболочки. А также поменять местами внутри одного объекта на массив, тогда это возможное решение.

 var data = [{"name": "JOHN","type": 1,"sum": 5},{"name": "SERA","type": 1,"sum": 43},{"name": "SERA","type": 2,"sum": 129},{"name": "JOHN","type": 2,"sum": 200}];

var newData = {};

data.forEach( (item) => {

  if (!(item['name'] in newData)) {
      newData[item['name']] = [];
  }
  newData[item['name']].push(
      {
        'type':  item['type'],
        'sum' : item['sum']
      }
  );
});

console.log(newData); 

Ответ №2:

Предлагаемая вами структура вывода недопустима, однако с помощью Array.reduce вы можете создать объект, в котором все свойства являются массивами объектов:

 const data = [
    {
        "name": "JOHN",
        "type": 1,
        "sum": 5
    },
    {
        "name": "SERA",
        "type": 1,
        "sum": 43
    },
    {
        "name": "SERA",
        "type": 2,
        "sum": 129
    },
    {
        "name": "JOHN",
        "type": 2,
        "sum": 200
    }
];

const result = data.reduce((c, {name, type, sum}) => {
  c[name] = c[name] || [];
  c[name].push({type, sum});
  return c;
}, {});
console.log(result); 

Ответ №3:

Еще один способ с forEach destructuring ?? помощью оператора and

 const merge = (arr) => {
  const obj = {};
  arr.forEach(({ name, ...rest }) => (obj[name] ??= []).push(rest));
  return obj;
};

const data = [
  {
    name: "JOHN",
    type: 1,
    sum: 5,
  },
  {
    name: "SERA",
    type: 1,
    sum: 43,
  },
  {
    name: "SERA",
    type: 2,
    sum: 129,
  },
  {
    name: "JOHN",
    type: 2,
    sum: 200,
  },
];

console.log(merge(data)); 

Ответ №4:

Вы можете использовать эту функцию, которая позволяет Array.prototype.reduce преобразовать исходные данные в другую структуру массива.

 let data = [
    {
        "name": "JOHN",
        "type": 1,
        "sum": 5
    },
    {
        "name": "SERA",
        "type": 1,
        "sum": 43
    },
    {
        "name": "SERA",
        "type": 2,
        "sum": 129
    },
    {
        "name": "JOHN",
        "type": 2,
        "sum": 200
    }
];

function groupedBy(data, field) {
  let fieldValues = [...data].reduce((acc, current) => {
    return acc.concat(current[field]);
  }, []).filter((value, index, self) => {
    return self.indexOf(value) === index;
  });
  
  let results = fieldValues.reduce((acc, item) => {
    let items = [...data].filter(el => {
      return el.name === item;
    });
    
    items.forEach(i => delete i.name);
    
    return Object.assign(acc, { [item]: items});
  }, {});
 
  return results;
}

console.log(groupedBy(data, "name"));