Объединение массивов в Javascript: аналог SQL JOIN операции
Быстрый ответ
Чтобы объединить массивы в JavaScript, подобно операции JOIN в SQL, используйте методы map
и find
для поиска соответствующих ключей:
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
сливаются в одну структуру данных.
Улучшение процесса соединения: повышаем производительность и гибкость
Соединение на максимальной скорости с помощью индексации
Индексы значительно ускоряют операции объединения в JavaScript. Давайте посмотрим, как можно проиндексировать массив для его соединения:
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, используя логические операторы:
// 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;
}, []);
Эти методы предоставляют нужную гибкость для решения различных задач.
Оптимизация преобразования массивов
Можно создать универсальную функцию для объединения массивов по ключу с возможностью выполнения кастомных преобразований:
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'!
Избирательное соединение
Можно объединить только определенные поля из каждого массива:
const selectiveJoin = users.map(user => {
const score = scoresMap.get(user.id);
const userScore = score ? score.score : undefined;
return { userName: user.name, userScore };
});
Визуализация
Представьте, что массивы – это поезда на параллельных рельсах:
Поезд A (🚂A): [🍎, 🍐, 🍋]
Поезд B (🚂B): [🍐, 🍋, 🍇]
Объединенный результат:
🚂A🔗🚂B: [🍎, 🍐, 🍋, 🍇]
Элементы массивов как вагоны, которые мы соединяем в один гигантский поезд с разнообразным грузом.
Мастерство изящного соединения
Создание функции соединения с индексацией
Можем автоматизировать процесс объединения и индексации через создание специальной функции:
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));
}
Применение:
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 для включения метода объединения:
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])));
};
И теперь применяем метод:
const joinedData = users.joinWith(scores, 'id', 'id', (user, score) => ({
...user,
score: score ? score.score : null
}));
Библиотечные средства: Прибегаем к помощи библиотек
Функции библиотек Underscore.js или Lodash могут значительно упростить работу с массивами:
const _ = require('underscore');
const underscoreJoined = _.map(users, user => {
return _.extend(user, _.findWhere(scores, { id: user.id }));
});
Полезные материалы
- Array.prototype.map() – JavaScript | MDN — руководство по преобразованию массивов.
- Array.prototype.filter() – JavaScript | MDN — описание функции фильтрации.
- Visual Representation of SQL Joins – CodeProject — визуальное описание операций SQL JOIN.
- Array.prototype.reduce() – JavaScript | MDN — описание функции сокращения массива.
- Lodash — библиотека для упрощения работы с массивами и объектами.
- Arrow function expressions – JavaScript | MDN — руководство по использованию стрелочных функций.