Сравните 2 массива объектов и удалите дубликаты

У меня есть 2 массива объектов в JavaScript, и я хотел бы сравнить и объединить содержимое и отсортировать результаты по идентификатору. В частности, результирующий отсортированный массив должен содержать все объекты из 1-го массива плюс все объекты из 2-го массива, которые имеют идентификатор, не входящий в 1-й.

Следующий код вроде работает (без сортировки). Но должен быть лучший и более лаконичный способ сделать это, особенно с функциями ES6. Я предполагаю, что использование Set - это правильный путь, но не знаю, как именно реализовать.

    var cars1 = [
        {id: 2, make: "Honda", model: "Civic", year: 2001},
        {id: 1, make: "Ford",  model: "F150",  year: 2002},
        {id: 3, make: "Chevy", model: "Tahoe", year: 2003},
    ];
    
    var cars2 = [
        {id: 3, make: "Kia",    model: "Optima",  year: 2001},
        {id: 4, make: "Nissan", model: "Sentra",  year: 1982},
        {id: 2, make: "Toyota", model: "Corolla", year: 1980},
    ];
    
    // Resulting cars1 contains all cars from cars1 plus unique cars from cars2
    cars1 = removeDuplicates(cars2);
    console.info(cars1);
    
    function removeDuplicates(cars2){
        for (entry in cars2) {
            var keep = true;
    
            for (c in cars1) {
                if (cars1[c].id === cars2[entry].id) {
                    keep = false;
                }
            }
    
            if (keep) {
                cars1.push({
                    id:cars2[entry].id,
                    make:cars2[entry].make,
                    model:cars2[entry].model,
                    year:cars2[entry].year
                })
            }
        }
        return cars1;
    }

Этот вопрос следует разместить в Обзор кода | StackExchange

AndrewL64 11.01.2019 08:34
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
12
1
11 727
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Одним из вариантов сложности O(N) было бы создание Set из id в cars1, затем распространение cars1 и отфильтрованного cars2 в выходной массив, с проверкой фильтра, включен ли id в автомобиле, который повторяется в cars2, в набор:

var cars1 = [
    {id: 2, make: "Honda", model: "Civic", year: 2001},
    {id: 1, make: "Ford",  model: "F150",  year: 2002},
    {id: 3, make: "Chevy", model: "Tahoe", year: 2003},
];

var cars2 = [
    {id: 3, make: "Kia",    model: "Optima",  year: 2001},
    {id: 4, make: "Nissan", model: "Sentra",  year: 1982},
    {id: 2, make: "Toyota", model: "Corolla", year: 1980},
];
const cars1IDs = new Set(cars1.map(({ id }) => id));
const combined = [
  ...cars1,
  ...cars2.filter(({ id }) => !cars1IDs.has(id))
];
console.info(combined);

Также на sort:

combined.sort(({ id: aId }, {id: bId }) => aId - bId);

var cars1 = [
    {id: 2, make: "Honda", model: "Civic", year: 2001},
    {id: 1, make: "Ford",  model: "F150",  year: 2002},
    {id: 3, make: "Chevy", model: "Tahoe", year: 2003},
];

var cars2 = [
    {id: 3, make: "Kia",    model: "Optima",  year: 2001},
    {id: 4, make: "Nissan", model: "Sentra",  year: 1982},
    {id: 2, make: "Toyota", model: "Corolla", year: 1980},
];
const cars1IDs = new Set(cars1.map(({ id }) => id));
const combined = [
  ...cars1,
  ...cars2.filter(({ id }) => !cars1IDs.has(id))
];
combined.sort(({ id: aId }, {id: bId }) => aId - bId);
console.info(combined);

Для этого идеально подходит объектный подход Set(). +1

AndrewL64 11.01.2019 08:40

Просто добавьте этот метод sort() в последнюю строку, например это, чтобы убедиться, что новый массив тоже отсортирован: combined.sort((a,b) => (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0));.

AndrewL64 11.01.2019 08:52

@CertainPerformance Это работает хорошо, но когда я удаленно удаляю элемент из БД, он должен быть удален из пользовательского интерфейса, но удаленный элемент застревает в пользовательском интерфейсе: \ НО, когда я добавляю новый элемент удаленно, он добавляется очень хорошо!

Oliver D 09.04.2020 01:42

@OliverD Ни в этом вопросе, ни в моем ответе нет БД или удаленных подключений. Если у вас есть новый вопрос, нажмите кнопку «Задать вопрос».

CertainPerformance 09.04.2020 05:36

Вы можете использовать concat, filter и map.

var cars1 = [ {id: 2, make: "Honda", model: "Civic", year: 2001}, {id: 1, make: "Ford", model: "F150", year: 2002}, {id: 3, make: "Chevy", model: "Tahoe", year: 2003}, ];

var cars2 = [ {id: 3, make: "Kia", model: "Optima", year: 2001}, {id: 4, make: "Nissan", model: "Sentra", year: 1982}, {id: 2, make: "Toyota", model: "Corolla", year: 1980}, ];

// Resulting cars1 contains all cars from cars1 plus unique cars from cars2
let ids = cars1.map(c => c.id);
cars1 = cars1.concat(cars2.filter(({id}) => !ids.includes(id)))
console.info(cars1);

Вы можете использовать Object.values() вместе с .concat() и .reduce():

let cars1 = [
    {id: 2, make: "Honda", model: "Civic", year: 2001},
    {id: 1, make: "Ford",  model: "F150",  year: 2002},
    {id: 3, make: "Chevy", model: "Tahoe", year: 2003},
];

let cars2 = [
    {id: 3, make: "Kia",    model: "Optima",  year: 2001},
    {id: 4, make: "Nissan", model: "Sentra",  year: 1982},
    {id: 2, make: "Toyota", model: "Corolla", year: 1980},
];

let merge = (arr1, arr2) => Object.values(
    arr1.concat(arr2).reduce((r, c) => (r[c.id] = r[c.id] || c, r), {})
).sort((a, b) => a.id - b.id);

console.info(merge(cars1, cars2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Объедините два массива, поместите каждый элемент массива на карту с их ids, а затем создайте массив из значений карты.

var cars1 = [
    {id: 2, make: "Honda", model: "Civic", year: 2001},
    {id: 1, make: "Ford",  model: "F150",  year: 2002},
    {id: 3, make: "Chevy", model: "Tahoe", year: 2003},
];

var cars2 = [
    {id: 3, make: "Kia",    model: "Optima",  year: 2001},
    {id: 4, make: "Nissan", model: "Sentra",  year: 1982},
    {id: 2, make: "Toyota", model: "Corolla", year: 1980},
];

cars = cars1.concat(cars2);
let foo = new Map();
for(const c of cars){
  foo.set(c.id, c);
}
let final = [...foo.values()]
console.info(final)

Вы можете взять Map и сначала взять элемент карты или сам автомобиль.

var cars1 = [{ id: 2, make: "Honda", model: "Civic", year: 2001 }, { id: 1, make: "Ford",  model: "F150",  year: 2002 }, { id: 3, make: "Chevy", model: "Tahoe", year: 2003 }],
    cars2 = [{ id: 3, make: "Kia",    model: "Optima",  year: 2001 }, { id: 4, make: "Nissan", model: "Sentra",  year: 1982 }, { id: 2, make: "Toyota", model: "Corolla", year: 1980 }],
    result = Array
        .from(
            [...cars1, ...cars2]
                .reduce((m, c) => m.set(c.id, m.get(c.id) || c), new Map)
                .values()
        )
        .sort((a, b) => a.id - b.id);

console.info(result);

Предполагая, что идентификатор должен быть уникальным, это должно работать:

var union = (arr1, arr2) => 
{
    var result = arr1.slice(0);
    arr2.forEach((el) => 
    { 
        if (getIndexByAttribute(arr1, 'id', el.id) < 0)
            result .push(el); 
    });
    return result;
};

var getIndexByAttribute = (array, attr, value) => {
    for(var i = 0; i < array.length; i += 1) {
        if (array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}

Но с вашими текущими примерами cars1 и cars2 вам может потребоваться объединение объектов. См. Сравнение объектов в JavaScript [дубликат]

Один из подходов может заключаться в использовании concat() с элементами cars2, чьи идентификаторы еще не находятся на cars1, это можно проверить с помощью find(). И наконец sort() получившийся массив:

var cars1 = [
    {id: 2, make: "Honda", model: "Civic", year: 2001},
    {id: 1, make: "Ford",  model: "F150",  year: 2002},
    {id: 3, make: "Chevy", model: "Tahoe", year: 2003},
];
    
var cars2 = [
    {id: 3, make: "Kia",    model: "Optima",  year: 2001},
    {id: 4, make: "Nissan", model: "Sentra",  year: 1982},
    {id: 2, make: "Toyota", model: "Corolla", year: 1980},
];

let res = cars1
    .concat(cars2.filter(({id}) => !cars1.find(x => x.id === id)))
    .sort((a, b) => a.id - b.id);

console.info(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Другие вопросы по теме