Объединить объекты массива с одинаковой датой в один - JavaScript - PullRequest
1 голос
/ 12 февраля 2020

Я получаю массив сообщений через API и хочу объединить сообщения с одинаковыми "month" и "year " (день не важен) в один объект. Я искал ответы, но есть слишком много примеров foo-bar, которые смущают больше, чем помогают. Я хочу знать самый чистый, самый элегантный способ решения таких проблем, не попадая в ад обратного вызова и вложенные блоки ...

Вот ответ API:

0:
{
  date: {day: 27, month: 1, year: 2020}
  id: 3
}
1:
{
  date: {day: 28, month: 1, year: 2020}
  id: 4
}
2:
{
 date: {day: 31, month: 1, year: 2020}
 id: 5
}
3:
{
 date: {day: 1, month: 2, year: 2020}
 id: 6
}
4:
{
 date: {day: 2, month: 2, year: 2020}
 id: 7
}

ожидаемый результат:

0:
result: {month: 1, year: 2020, id:[3,4,5]}
1:
result: {month: 2, year: 2020, id:[6,7]}

Ответы [ 2 ]

2 голосов
/ 12 февраля 2020

Один из подходов заключается в использовании метода Array#reduce() для преобразования входного массива в словарь, где каждое значение содержит накопление идентификаторов для этого месяца и года. После того, как этот словарь был создан, вы можете извлечь значения этого словаря в массив с помощью Object#values(), чтобы получить требуемый вывод:

let input=[{date:{day:27,month:1,year:2020},id:3},{date:{day:28,month:1,year:2020},id:4},{date:{day:31,month:1,year:2020},id:5},{date:{day:1,month:2,year:2020},id:6},{date:{day:2,month:2,year:2020},id:7}];

/* Convert the dictionary that will be created by reduce to a value array */
var output = Object.values(input.reduce((dict, item) => {
  
  const { date, id } = item;

  /* The distinct key for this item based on month/year of date field */
  const key = `${date.month}-${date.year}`;
  
  /* Check if dictionary already has an object value for key. This short hand
  will only insert a new object value for key, if one does not already exist
  in the dictionary */
  const value = dict[key] || { month : date.month, year : date.year, id : [] };

  /* Add the item id to the dictionary entries id array */
  value.id.push(id);
  
  /* Update value object for key */
  return { ...dict, [key] : value };
  
}, {}))

console.log(output);

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

Если для текущего ключа не существует значения, в словарь для этого ключа добавляется новый объект значения:

{ month : date.month, year : date.year, id : [] }

Затем добавляется id текущего элемента массива ( ) к массиву id объекта для этого ключа:

dict[key].id.push(id);

Надеюсь, что поможет

1 голос
/ 12 февраля 2020

Вот альтернативный подход, если вы не большой поклонник Array.reduce и Array.values, а также, если вы хотите учитывать производительность при выполнении ответа для большого набора данных.

Этот подход позволяет избежать клонирования объекта (или, скорее, не мутирующего объекта) с оператором распространения i.e {...<anyObject>} во время итерации. что подходит для минимального набора данных, но определенно не для больших объемов.

const response = [{
  date: { day: 27, month: 1, year: 2020 },
  id: 3
}, {
  date: { day: 28, month: 1, year: 2020 },
  id: 4
}, {
  date: { day: 31, month: 1, year: 2020 },
  id: 5
},{
  date: { day: 1, month: 2, year: 2020 },
  id: 6
},{
  date: { day: 2, month: 2, year: 2020 },
  id: 7
}];


function groupByMonthYear(response) {
  // output
  const groupedData = []
  
  // Using map for lookup to avoid iterating again on the grouped data
  const referenceMap = new Map();

  // destructing month, year and id from the response
  for (const { date: { month, year }, id } of response) {
    const groupKey = `${month}${year}`

    // check if the month and year reference is already seen using the groupKey MMYYYY
    if (referenceMap.has(groupKey)) {
      referenceMap.get(groupKey).id.push(id);
      // early return
      continue;
    }

    // simply add a new entry if it doesn't exist
    const data = {
      month,
      year,
      id: [id]
    };

    groupedData.push(data);
    referenceMap.set(groupKey, data)
  }

  return groupedData;
}

// Invoke and Print the result
console.log(groupByMonthYear(response));
...