t-1000
@t-1000
JavaScript, React, Nodejs

Как объединить два массива в один у которых ключи id и user_id с одинаковыми значениями в js?

Есть "большой" массив, в котором user_id повторяются:
[
 { "user_id": 1, "page_views": 7, "clicks": 5 },
 { "user_id": 5, "page_views": 6, "clicks": 3 },
 { "user_id": 9, "page_views": 4, "clicks": 7 },
 { "user_id": 1, "page_views": 3, "clicks": 5 },
]

Есть "маленький" массив, id все уникальные:
[
 {"id":9,"first_name":"Barnabas"},
 {"id":1,"first_name":"Emlyn"},
 {"id":5,"first_name":"Ervin"},
]

Нужно слить их в общий массив, при этом отыскать все совпавшие значения id маленького массива c user_id большого
и сумму всех ключей page_views и clicks добавить в объекты маленького.

Вид массива должен быть следующий:
[
 {"id":9, "first_name":"Barnabas", "page_views": 4 , "clicks": 7 },
 {"id":1, "first_name":"Emlyn", "page_views": 10, "clicks": 10 },
 {"id":5, "first_name":"Ervin", "page_views": 7, "clicks": 5 },
]
  • Вопрос задан
  • 4079 просмотров
Решения вопроса 3
0xD34F
@0xD34F Куратор тега JavaScript
Object.values(arr1.reduce((acc, { user_id: id, ...n }) => {
  Object.entries(n).forEach(([ k, v ]) => acc[id][k] = (acc[id][k] || 0) + v);
  return acc;
}, Object.fromEntries(arr2.map(n => [ n.id, {...n} ]))))

если надо, чтобы в исходном и результирующем массивах элементы с одинаковыми id располагались на одних и тех же местах, то вместо обычного объекта собираем данные в Map:

[...arr1.reduce((acc, { user_id: id, ...n }) => {
  const obj = acc.get(id);
  Object.entries(n).forEach(([ k, v ]) => obj[k] = (obj[k] || 0) + v);
  return acc;
}, new Map(arr2.map(n => [ n.id, {...n} ]))).values()]

или, вместо Object.values извлекаем данные из объекта через map по исходному массиву:

arr2.map(function(n) {
  return this[n.id];
}, arr1.reduce((acc, { user_id: id, ...n }) => (
  Object.keys(n).forEach(k => acc[id][k] = (acc[id][k] || 0) + n[k]),
  acc
), Object.fromEntries(arr2.map(n => [ n.id, {...n} ]))))
Ответ написан
Комментировать
contraomnes
@contraomnes
Frontend developer
вот тебе ещё
const array1 = [
{ "user_id": 1, "page_views": 7, "clicks": 5 },
{ "user_id": 5, "page_views": 6, "clicks": 3 },
{ "user_id": 9, "page_views": 4, "clicks": 7 },
{ "user_id": 1, "page_views": 3, "clicks": 5 },
];

const array2 = [
{"id":9,"first_name":"Barnabas"},
{"id":1,"first_name":"Emlyn"},
{"id":5,"first_name":"Ervin"},
];

const result = array2.map(item2 => {
  // отфильтровали массив 1 на наличие элементов с соответствующим id
  const withCurrentId = array1.filter(item1 => item1['user_id'] === item2['id']);
  
  // склеили новый объект с суммами свойств
  const item1 = withCurrentId.reduce((acc, curr) => {
    // если в аккумуляторе нет свойства page_views, то 0, потом суммируем
    acc['page_views'] = (acc['page_views'] || 0) + curr['page_views'];
    // если в аккумуляторе нет свойства clicks, то 0, потом суммируем
    acc['clicks'] = (acc['clicks'] || 0) + curr['clicks'];

    return acc;
  }, {});

  return { ...item2, ...item1 };
});


console.log(result);
Ответ написан
Комментировать
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
let result = users.reduce(
  (acc, val) => {
    acc[val.id] = {
      id: val.id,
      first_name: val.first_name,
      page_views: 0,
      clicks: 0
    };
    return acc;
  },
  []
);
result = views.reduce(
  (acc, val) => {
    acc[val.user_id].page_views += val.page_views;
    acc[val.user_id].clicks += val.clicks;
    return acc;
  },
  result
);
result = result.filter(x => x);
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
@vasiiil
С ходу на коленке как-то так:
for (let i in users) {
	for (let j in views) {
		if (users[i].id === views[j].user_id) {
			if (users[i].hasOwnProperty('page_views')) {
				users[i].page_views += views[j].page_views;
				users[i].clicks += views[j].clicks;
			}
			else {
				users[i].page_views = views[j].page_views;
				users[i].clicks = views[j].clicks;
			}
		}
	}
}

Но возможно кто-то подскажет более элегантное решение.
И здесь не рассматривается случай, если в массиве users нет юзера с id из массива views.
Ответ написан
Комментировать
примерно так
let arr = []
arrBig.forEach((elem) => {
let clicks = (typeof arr[elem.user_id] !== "undefined") ? arr[elem.user_id].clicks + elem.clicks : elem.clicks;
let page_views = (typeof arr[elem.user_id] !== "undefined") ? arr[elem.user_id].page_views + elem.page_views : elem.page_views;
let row = { "page_views": page_views, "clicks": clicks }
arr[elem.user_id] = Object.assign((arr[elem.user_id] || {}), row);
})

arrSmall.map((item) => {
return {
id: item.id,
first_name: item.first_name,
page_views: (typeof arr[item.user_id] !== "undefined") ? arr[item.user_id].page_views : 0,
clicks: (typeof arr[item.user_id] !== "undefined") ? arr[item.user_id].page_views : 0,
};
});
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
28 мар. 2024, в 21:25
2000 руб./за проект
28 мар. 2024, в 21:17
5000 руб./за проект
28 мар. 2024, в 20:46
150000 руб./за проект