编辑代码

class Comparator {
    constructor() {
        this.cache = new WeakMap();
        this.patternCache = new Map();
        this.configCache = new Map();
    }

    // 主比较方法
    compare(a, b, config = {}) {
        return this._coreCompare(a, b, {
            path: '',
            depth: 0,
            filterObjectKeys: this._normalizePatterns(config.filterObjectKeys || []),
            filterArrayKeys: config.filterArrayKeys || {}
        });
    }

    // 核心比较逻辑
    _coreCompare(a, b, ctx) {
        console.log('a, b, ctx')
        // 短路检查1:路径过滤
        if (this._isFiltered(ctx.path, ctx.filterObjectKeys)) return true;

        // 短路检查2:严格相等
        if (Object.is(a, b)) return true;

        // 类型一致性检查
        const aType = this._getType(a);
        const bType = this._getType(b);
        if (aType !== bType) return false;

        // 对象缓存检查
        if (this._isObject(a)) {
            const cached = this._getCache(a, b);
            if (cached !== undefined) return cached;
        }

        let result;
        switch (true) {
            case Array.isArray(a):
                result = this._compareArrays(a, b, ctx);
                break;
            case a instanceof Date:
                result = a.getTime() === b.getTime();
                break;
            case this._isObject(a):
                result = this._compareObjects(a, b, ctx);
                break;
            default:
                result = false;
        }

        // 写入缓存
        if (this._isObject(a)) this._setCache(a, b, result);
        return result;
    }

    // 对象比较(支持无限层级)
    _compareObjects(objA, objB, ctx) {
        const keys = new Set([...Object.keys(objA), ...Object.keys(objB)]);

        return Array.from(keys).every(key => {
            const newCtx = this._updateContext(ctx, key);

            // 短路检查:子路径过滤
            if (this._isFiltered(newCtx.path, newCtx.filterObjectKeys)) return true;

            return this._coreCompare(objA[key], objB[key], newCtx);
        });
    }

    // 数组比较(支持多维数组)
    _compareArrays(arrA, arrB, ctx) {
        // 短路检查:数组路径过滤
        if (this._isFiltered(ctx.path, ctx.filterObjectKeys)) return true;

        const config = this._getArrayConfig(ctx);
        const [filteredA, filteredB] = this._filterArrays(arrA, arrB, config);

        // 长度检查
        if (filteredA.length !== filteredB.length) return false;

        // 顺序敏感模式
        if (config.arrayOrderMatters) {
            return filteredA.every((item, i) =>
                this._coreCompare(item, filteredB[i], this._updateContext(ctx, i))
            );
        }

        // 顺序不敏感模式(哈希计数优化)
        return this._unorderedCompare(filteredA, filteredB, ctx);
    }

    // 工具方法
    _getType(obj) {
        return Object.prototype.toString.call(obj);
    }

    _isObject(v) {
        return v !== null && typeof v === 'object' && !Array.isArray(v);
    }

    _updateContext(ctx, segment) {
        const path = ctx.path ? `${ctx.path}.${segment}` : segment.toString();
        return {
            ...ctx,
            path,
            depth: ctx.depth + 1
        };
    }

    // 缓存管理
    _getCache(a, b) {
        return this.cache.get(a) ?.get(b) || this.cache.get(b) ?.get(a);
    }

    _setCache(a, b, value) {
        if (!this.cache.has(a)) this.cache.set(a, new WeakMap());
        this.cache.get(a).set(b, value);
    }

    // 数组配置获取
    _getArrayConfig(ctx) {
        const cached = this.configCache.get(ctx.path);
        if (cached) return cached;

        for (const [pattern, config] of Object.entries(ctx.filterArrayKeys)) {
            if (this._matchPattern(ctx.path, pattern)) {
                this.configCache.set(ctx.path, config);
                return config;
            }
        }
        return {};
    }

    // 路径匹配系统
    _normalizePatterns(patterns) {
        return patterns.map(p => p.replace(/\.\*\*/g, '.[^.]+(\\..*)?'));
    }

    _isFiltered(path, patterns) {
        return patterns.some(pattern => this._matchPattern(path, pattern));
    }

    _matchPattern(path, pattern) {
        const normalized = pattern
            .replace(/$$(\d+)$$/g, '.$1')   // 转换数组索引
            .replace(/\*/g, '[^.]+')        // 单级通配符
            .replace(/\.\*\*/g, '.[^.]+(\\..*)?'); // 多级通配符

        const regex = new RegExp(`^${normalized}$`);
        return regex.test(path);
    }

    // 数组过滤优化
    _filterArrays(arrA, arrB, config) {
        const filter = new Set(config.filterArrayValues || []);
        return [
            arrA.filter((_, i) => !filter.has(i)),
            arrB.filter((_, i) => !filter.has(i))
        ];
    }

    // 无序比较优化
    _unorderedCompare(a, b, ctx) {
        const map = new Map();
        a.forEach(item => {
            const key = this._createHashKey(item, ctx);
            map.set(key, (map.get(key) || 0) + 1);
        });

        return b.every(item => {
            const key = this._createHashKey(item, ctx);
            const count = map.get(key) || 0;
            if (count === 0) return false;
            map.set(key, count - 1);
            return true;
        });
    }

    _createHashKey(item, ctx) {
        return JSON.stringify(item, (_, v) => {
            if (this._isObject(v)) return '';  // 简化对象结构
            return v;
        }) + '|' + ctx.depth;  // 添加深度标识
    }
}


// const comparator1 = new Comparator();
// const snapshotTable = [[{ a: 1, b: 2 }], [{ a: 2, b: 3 }]]
// const table = [[{ a: 1, b: 2 }], [{ a: 2, b: 5 }]]

// const omit1 = {
//     filterObjectKeys: ['[*][*].b'], // 忽略 age 字段
// };

// console.log(comparator1.compare(snapshotTable, table, omit1))

// const comparator2 = new Comparator();
// const snapshotForm = { a: 2, b: 3 }
// const form1 = { a: 2, b: 5 }

// const omit2 = {
//     filterObjectKeys: ['b'], // 忽略 age 字段
// };

// console.log(comparator1.compare(snapshotForm, form1, omit2))

// 使用示例
const comparator = new Comparator();

const snapshot = {
    name: "John1",
    age: 30,
    hobbies: [
        { name: "reading", level: "high" },
        { name: "coding", level: "medium" },
    ],
    arr: [[{ a: 1, b: 2 }], [{ a: 2, b: 3 }]],
    address: {
        city: "New York",
        zip: "10001"
    },
    orderArr: ['a', 'v', 's', 'd'],
    orderArrObj: [{ a: 1, b: 2 }, { a: 2, b: 3 }],
    users: [
        { id: 1, name: 'Alice', permissions: new Set(['read']) },
        { id: 2, name: 'Bob' }
    ],
    meta: { timestamp: new Date('2023-01-01') }
};

const form = {
    name: "John",
    age: 30,
    hobbies: [
        { name: "reading", level: "high" },
    ],
    arr: [[{ a: 1, b: 2 }], [{ a: 2, b: 5 }]],
    address: {
        city: "New York",
        zip: "10002"
    },
    orderArr: ['a', 'v', 'd', 's'],
    orderArrObj: [{ a: 1, b: 2 }, { a: 2, b: 5 }],
    users: [
        { id: 2, name: 'Bob' },
        { id: 1, name: 'Alice', permissions: new Set(['read']) }
    ],
    meta: { timestamp: new Date('2023-01-01') }
};

const omit = {
    filterObjectKeys: ["age", 'hobbies[*].level', 'arr', 'orderArr', 'address.zip', 'orderArrObj', 'users', 'meta'], // 忽略 age 字段
    filterArrayKeys: {
        "hobbies": { // 对 hobbies 下的所有对象应用相同的规则
            filterArrayValues: [1]
        },
    }
};

const result = comparator.compare(snapshot, form, omit);

console.log(result); // true