You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对比含对象数组的复杂JS对象并找出差异(不硬编码属性)

复杂对象差异检测:支持数组内对象任意顺序的实现

需求

实现函数对比两个包含对象数组的复杂JavaScript对象,找出updatedObj相对baseObj的差异,要求不能硬编码属性,且能正确处理数组内对象顺序变化的情况。

示例数据

let baseObj = {
    place: {city: 'Bangalore', pin: 123456},
    office: [
        { name: 'Tom', age: 22, salutation: { title: 'Mr'}},
        { name: 'John', age: 31, salutation: { title: 'Mr'}}
    ]
} 
let updatedObj = {
    place: {city: 'Bangalore', pin: 99999},
    office: [
        { name: 'Tom', age: 22, salutation: { title: 'Mr'}},
        { name: 'Peter', age: 16, salutation: { title: 'Mr'}},
        { name: 'John', age: 31, salutation: { title: 'Mr'}}
    ]
}

预期结果

const expectedResult = {
   place: {city: 'Bangalore', pin: 99999},
   office: [
       { name: 'Peter', age: 16, salutation: { title: 'Mr'}}
   ]
}

现有代码问题

当前函数仅按数组索引对比元素,当数组内对象顺序变化时会误判,且存在拼写错误(lenght应为length)和逻辑缺陷。原代码如下:

function findDiff(obj1, obj2) {
    var diffObj = Array.isArray(obj2) ? [] : {}
    Object.getOwnPropertyNames(obj2).forEach(function(prop) {
        if(prop !=='lenght' ){
        if (typeof obj2[prop] === 'object') {
            diffObj[prop] = obj1[prop]== undefined? obj2[prop]: findDiff(obj1[prop], obj2[prop])
            if (Array.isArray(diffObj[prop]) && Object.getOwnPropertyNames(diffObj[prop]).length === 1 || Object.getOwnPropertyNames(diffObj[prop]).length === 0) {
                delete diffObj[prop]
            }
        }} else if(prop !=='lenght') {
        
        if(obj1[prop] !== obj2[prop]){
            diffObj[prop] = obj2[prop]
            }
        }
    });
    return diffObj
}

解决方案

核心思路:

  • 实现深比较函数,判断两个对象/值是否完全相等,用于检测数组元素是否存在于另一个数组中。
  • 数组处理逻辑:遍历updated数组,筛选出base数组中不存在的元素(新增),以及存在但内部属性有修改的元素。
  • 对象处理逻辑:递归对比每个属性,可选择仅保留差异属性或合并原属性与差异(贴合示例预期)。

完整实现代码

// 深比较函数:判断两个值是否完全相等
function deepEqual(a, b) {
    // 基本类型直接比较
    if (a === b) return true;

    // 处理null/undefined情况
    if (a == null || b == null) return false;

    // 类型不同直接返回false
    if (typeof a !== typeof b) return false;

    // 处理数组
    if (Array.isArray(a) && Array.isArray(b)) {
        if (a.length !== b.length) return false;
        for (let i = 0; i < a.length; i++) {
            if (!deepEqual(a[i], b[i])) return false;
        }
        return true;
    }

    // 处理对象
    if (typeof a === 'object' && typeof b === 'object') {
        const keysA = Object.keys(a);
        const keysB = Object.keys(b);
        if (keysA.length !== keysB.length) return false;
        for (const key of keysA) {
            if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false;
        }
        return true;
    }

    return false;
}

// 差异检测主函数
function findDiff(baseObj, updatedObj) {
    // 处理基本类型
    if (typeof updatedObj !== 'object' || updatedObj == null) {
        return baseObj !== updatedObj ? updatedObj : undefined;
    }

    // 初始化差异结果
    const diff = Array.isArray(updatedObj) ? [] : {};

    // 处理数组
    if (Array.isArray(updatedObj)) {
        // 遍历updated数组的每个元素
        updatedObj.forEach(updatedItem => {
            // 检查base数组中是否存在完全相等的元素
            const existsInBase = Array.isArray(baseObj) && baseObj.some(baseItem => deepEqual(baseItem, updatedItem));
            
            if (!existsInBase) {
                // 元素不存在于base中,直接加入差异数组
                diff.push(updatedItem);
            } else {
                // 元素存在,但检查是否有内部属性修改
                const baseItem = baseObj.find(item => deepEqual(item, updatedItem));
                const itemDiff = findDiff(baseItem, updatedItem);
                // 如果有差异,加入修改后的元素
                if (itemDiff !== undefined && Object.keys(itemDiff).length > 0) {
                    diff.push({...baseItem, ...itemDiff});
                }
            }
        });
        // 如果差异数组为空,返回undefined(后续会被清理)
        return diff.length > 0 ? diff : undefined;
    }

    // 处理对象:合并原属性与差异,贴合示例预期保留未修改属性
    Object.keys(updatedObj).forEach(key => {
        const baseVal = baseObj[key];
        const updatedVal = updatedObj[key];

        const valDiff = findDiff(baseVal, updatedVal);
        // 如果有差异,合并原属性和差异(保留未修改的属性)
        if (valDiff !== undefined) {
            if (typeof updatedVal === 'object' && updatedVal != null && !Array.isArray(updatedVal)) {
                diff[key] = {...baseVal, ...valDiff};
            } else {
                diff[key] = valDiff;
            }
        }
    });

    // 如果对象差异为空,返回undefined
    return Object.keys(diff).length > 0 ? diff : undefined;
}

// 测试示例
const result = findDiff(baseObj, updatedObj);
console.log(result);
// 输出与预期结果一致

代码说明

  1. deepEqual函数:递归对比两个值的所有层级,支持数组和嵌套对象的精准比较,解决了原代码按索引对比数组的缺陷。
  2. 数组处理:
    • 遍历updated数组,用deepEqual判断元素是否存在于base数组中,不受顺序影响。
    • 不存在的元素直接标记为新增,加入差异数组。
    • 存在的元素进一步检查内部属性差异,若有修改则合并原属性与差异后加入结果。
  3. 对象处理:递归对比每个属性,通过合并原属性与差异的方式,保留嵌套对象中未修改的属性,完全贴合示例预期。
  4. 空结果清理:如果某个属性/数组没有差异,返回undefined,最终结果不会包含空对象或空数组,保证输出简洁。

内容的提问来源于stack exchange,提问作者akh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:41:20