Объединение массивов в Javascript: аналог SQL JOIN операции

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Чтобы объединить массивы в JavaScript, подобно операции JOIN в SQL, используйте методы map и find для поиска соответствующих ключей:

JS
Скопировать код
const users = [{ id: 1, name: 'Алиса' }, { id: 2, name: 'Боб' }];
const scores = [{ id: 1, score: 90 }, { id: 2, score: 80 }];

const joined = users.map(user => ({
  ...user,
  ...scores.find(score => score.id === user.id)
}));

// Результат: [{ id: 1, name: 'Алиса', score: 90 }, { id: 2, name: 'Боб', score: 80 }]

Итак, объекты с совпадающим id сливаются в одну структуру данных.

Кинга Идем в IT: пошаговый план для смены профессии

Улучшение процесса соединения: повышаем производительность и гибкость

Соединение на максимальной скорости с помощью индексации

Индексы значительно ускоряют операции объединения в JavaScript. Давайте посмотрим, как можно проиндексировать массив для его соединения:

JS
Скопировать код
const users = [{ id: 1, name: 'Алиса' }, { id: 2, name: 'Боб' }];
const scoresMap = new Map(scores.map(score => [score.id, score]));

const joined = users.map(user => ({
  ...user,
  ...(scoresMap.get(user.id) || {})
}));

// С использованием Map мы теперь достигаем производительности порядка O(m + n)!

Обработка случаев, когда нет соответствий: "Неуловимые записи"

Если ключи не совпадают, мы можем реализовать аналоги операций left join или inner join, используя логические операторы:

JS
Скопировать код
// Left join
const leftJoined = users.map(user => ({
  ...user,
  score: scoresMap.get(user.id)?.score 
}));

// Inner join
const innerJoined = users.reduce((accum, user) => {
  const score = scoresMap.get(user.id);
  if (score) {
    accum.push({ ...user, score: score.score });
  }
  return accum;
}, []);

Эти методы предоставляют нужную гибкость для решения различных задач.

Оптимизация преобразования массивов

Можно создать универсальную функцию для объединения массивов по ключу с возможностью выполнения кастомных преобразований:

JS
Скопировать код
function joinArraysByKey(array1, array2, key, transform) {
  const index = new Map(array2.map(item => [item[key], transform(item)]));
  return array1.map(item => ({
    ...item,
    ...index.get(item[key])
  }));
}

const transformScore = score => ({ userScore: score.score });
const streamlinedJoin = joinArraysByKey(users, scores, 'id', transformScore);

// Мы преобразовали свойство 'score' в 'userScore'!

Избирательное соединение

Можно объединить только определенные поля из каждого массива:

JS
Скопировать код
const selectiveJoin = users.map(user => {
  const score = scoresMap.get(user.id);
  const userScore = score ? score.score : undefined;
  return { userName: user.name, userScore };
});

Визуализация

Представьте, что массивы – это поезда на параллельных рельсах:

Markdown
Скопировать код
Поезд A (🚂A): [🍎, 🍐, 🍋]
Поезд B (🚂B): [🍐, 🍋, 🍇]

Объединенный результат:

Markdown
Скопировать код
🚂A🔗🚂B: [🍎, 🍐, 🍋, 🍇]

Элементы массивов как вагоны, которые мы соединяем в один гигантский поезд с разнообразным грузом.

Мастерство изящного соединения

Создание функции соединения с индексацией

Можем автоматизировать процесс объединения и индексации через создание специальной функции:

JS
Скопировать код
function equiJoin(primary, secondary, primaryKey, secondaryKey, select) {
  const index = new Map(primary.map(item => [item[primaryKey], item]));
  return secondary.filter(item => index.has(item[secondaryKey]))
                  .map(item => select(index.get(item[secondaryKey]), item));
}

Применение:

JS
Скопировать код
const userFields = user => ({ id: user.id, name: user.name });
const scoreFields = score => ({ score: score.score });
const usersWithScores = equiJoin(users, scores, 'id', 'id', (user, score) => ({
  ...userFields(user),
  ...scoreFields(score)
}));

Встроенные методы объединения

Добавим прототип Array для включения метода объединения:

JS
Скопировать код
Array.prototype.joinWith = function(otherArray, thisKey, otherKey, select) {
  const index = new Map(otherArray.map(item => [item[otherKey], item]));
  return this.map(item => select(item, index.get(item[thisKey])));
};

И теперь применяем метод:

JS
Скопировать код
const joinedData = users.joinWith(scores, 'id', 'id', (user, score) => ({
  ...user,
  score: score ? score.score : null
}));

Библиотечные средства: Прибегаем к помощи библиотек

Функции библиотек Underscore.js или Lodash могут значительно упростить работу с массивами:

JS
Скопировать код
const _ = require('underscore');

const underscoreJoined = _.map(users, user => {
  return _.extend(user, _.findWhere(scores, { id: user.id }));
});

Полезные материалы

  1. Array.prototype.map() – JavaScript | MDN — руководство по преобразованию массивов.
  2. Array.prototype.filter() – JavaScript | MDN — описание функции фильтрации.
  3. Visual Representation of SQL Joins – CodeProject — визуальное описание операций SQL JOIN.
  4. Array.prototype.reduce() – JavaScript | MDN — описание функции сокращения массива.
  5. Lodash — библиотека для упрощения работы с массивами и объектами.
  6. Arrow function expressions – JavaScript | MDN — руководство по использованию стрелочных функций.