比较两个对象数组并删除重复项

12
我有两个JavaScript对象数组,希望将它们进行比较和合并,并按id排序结果。具体来说,排序后的数组应该包含第一个数组中的所有对象,再加上第二个数组中所有id不在第一个数组中的对象。
以下代码似乎可以实现(除了排序)。但是肯定有更好,更简洁的方法来完成这个任务,尤其是使用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.log(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;
    }


2
这个问题应该在 Code Review | StackExchange 上发布。 - AndrewL64
7个回答

15

一种时间复杂度为O(N)的选项是在cars1中创建一个idSet,然后将cars1和经过筛选的cars2扩展到输出数组中,并通过测试筛选器来检查cars2中正在迭代的汽车中的id是否包含在集合中:

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.log(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.log(combined);


2
Set() 对象的方法非常适合这种情况。+1 - AndrewL64
只需在最后一行添加此 sort() 方法,就像这样,以确保新数组也已排序:combined.sort((a,b) => (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0)); - AndrewL64
@CertainPerformance 它能够正常工作,但是当我远程从DB中删除一个项目时,它应该从UI中删除,但已删除的项目却停留在UI中:\ 但是,当我远程添加新项目时,它添加得非常好! - Oliver D
@OliverD 这个问题和我的回答中都没有涉及到数据库或远程连接。如果你有新的问题,请点击“提问”按钮。 - CertainPerformance

2
您可以使用concatfiltermap来实现。

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.log(cars1);


2
合并两个数组,将每个数组元素与其id一起放入映射中,然后从映射值创建数组。

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.log(final)


2
你可以先取出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.log(result);


1
你可以使用 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.log(merge(cars1, cars2));
.as-console-wrapper { max-height: 100% !important; top: 0; }


1
一种方法是使用 concat() 函数,将 cars2 中 id 不在 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.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}


1
假设id应该是唯一的,那么这个代码应该可以工作:
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;
}

但是根据你目前的示例cars1cars2,你可能需要进行对象比较。请参见JavaScript中的对象比较[重复]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接