如何用高效函数式编程实现非原始值数组B相对于A的相对补集?
解决对象数组的相对补集问题(B相对于A)
这问题我做项目时也碰到过,确实_.without只支持原始值数组,完全没法处理对象的情况。给你两个高效的函数式解决方案,刚好匹配你的需求:
方案一:用Lodash原生方法_.differenceWith
Lodash其实有专门处理对象数组差异的方法,就是_.differenceWith,它支持传入自定义比较器函数,完美适配这个场景:
const _ = require('lodash'); const arrayA = [{ id: 1 }, { id: 2 }]; const arrayB = [{ id: 2 }, { id: 3 }]; // 用_.isEqual做深度比较,也可以自定义对比逻辑(比如只匹配id) const relativeComplement = _.differenceWith(arrayB, arrayA, _.isEqual); console.log(relativeComplement); // 输出: [{ id: 3 }]
如果你的匹配逻辑只需要对比id而非整个对象,还可以写个更轻量的比较器:
const relativeComplement = _.differenceWith(arrayB, arrayA, (a, b) => a.id === b.id);
这个方法是Lodash优化过的,性能比自己手写循环靠谱得多。
方案二:原生JavaScript函数式实现(不依赖Lodash)
如果不想用第三方库,原生JS也能写出高效的函数式代码,核心思路是先把数组A的标识(比如id)存入Set,再过滤数组B:
const arrayA = [{ id: 1 }, { id: 2 }]; const arrayB = [{ id: 2 }, { id: 3 }]; // 第一步:提取A中所有id存入Set,O(n)时间复杂度 const aIds = new Set(arrayA.map(item => item.id)); // 第二步:过滤B中不在A里的元素,O(m)时间复杂度 const relativeComplement = arrayB.filter(item => !aIds.has(item.id)); console.log(relativeComplement); // 输出: [{ id: 3 }]
这个方案的时间复杂度是O(n+m),效率拉满。如果需要对比整个对象而非单个字段,可以把对象转成JSON字符串存入Set(注意要保证对象键顺序一致,否则会被判定为不同):
const aItemStrs = new Set(arrayA.map(item => JSON.stringify(item))); const relativeComplement = arrayB.filter(item => !aItemStrs.has(JSON.stringify(item)));
要是担心键顺序的问题,也可以结合深度比较函数过滤(但这种方式时间复杂度是O(n*m),数组较大时不推荐):
const relativeComplement = arrayB.filter(bItem => !arrayA.some(aItem => _.isEqual(aItem, bItem)));
总结
- 已经在用Lodash的话,优先用
_.differenceWith,简洁又高效 - 原生场景下,Set+filter的方案性能最优,完全符合函数式编程的风格
内容的提问来源于stack exchange,提问作者LiranC
相关产品推荐
相关产品推荐

