es-toolkit 的 differenceWith 指南用自定义比较函数精确计算数组差集【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkitdifferenceWith是 es-toolkit 数组模块中用于求两个数组差集difference的函数与基于严格相等判断的difference不同它允许你传入自定义比较函数areItemsEqual从而按对象属性、模糊规则或跨类型规则判断两个元素是否相同。本指南将基于 docs/ja/reference/array/differenceWith.md 的官方文档结合仓库中的源码与测试讲解它的签名、典型用法、实现原理以及与 lodash 兼容版es-toolkit/compat的差异读完后你可以在对象数组去重、按业务键求差集等场景中直接落地使用。函数签名与核心概念differenceWith的调用形式如下const result differenceWith(firstArr, secondArr, areItemsEqual);它接收两个数组和一个比较函数返回一个新数组其中包含按比较函数判定只存在于第一个数组中的元素。参数类型说明firstArrT[]求差集时的基准数组secondArrU[]包含要从第一个数组中排除的元素的数组areItemsEqual(x: T, y: U) boolean判断两个元素是否相同的函数返回值T[]—— 根据比较函数被判定为仅存在于第一个数组的元素组成的新数组。注意三个参数使用了两个泛型T和U意味着firstArr和secondArr可以是不同类型的数组这为跨类型比较如对象数组与基本类型数组求差留出了类型安全的空间。该函数已在 src/array/index.ts 中作为 es-toolkit 数组 API 的一部分对外导出可通过import { differenceWith } from es-toolkit/array引入。基础用法按对象属性求差集最常见的场景是对象数组之间按某个业务字段如id判断是否相同import { differenceWith } from es-toolkit/array; // 对象数组中以 id 为基准求差集 const array1 [{ id: 1 }, { id: 2 }, { id: 3 }]; const array2 [{ id: 2 }, { id: 4 }]; const areItemsEqual (a, b) a.id b.id; differenceWith(array1, array2, areItemsEqual); // 返回: [{ id: 1 }, { id: 3 }] // id 为 2 的元素被判定为相同因此被排除在这个例子中array1里的{ id: 2 }与array2里的{ id: 2 }虽然是两个不同的对象引用但比较函数判定它们相等因此被从结果中剔除{ id: 4 }只存在于array2中本来就不在基准数组中不影响结果。比较不同类型的数组因为firstArr与secondArr允许不同类型你可以让对象数组与数字数组互相比较import { differenceWith } from es-toolkit/array; const objects [{ id: 1 }, { id: 2 }, { id: 3 }]; const numbers [2, 4]; const areItemsEqual2 (a, b) a.id b; differenceWith(objects, numbers, areItemsEqual2); // 返回: [{ id: 1 }, { id: 3 }]这里比较函数把对象的id字段与数组中的数字直接比较{ id: 2 }与2被判为相等从而被排除。复杂条件比较自定义业务相等规则比较函数的自由度使你可以实现任何业务语义。下面的例子中即使两个人的年龄不同只要名字相同就被视为同一个人import { differenceWith } from es-toolkit/array; const users1 [ { name: Alice, age: 30 }, { name: Bob, age: 25 }, { name: Charlie, age: 35 }, ]; const users2 [ { name: Alice, age: 31 }, // 年龄不同但名字相同则视为同一用户 { name: David, age: 25 }, ]; const areUsersEqual (a, b) a.name b.name; differenceWith(users1, users2, areUsersEqual); // 返回: [{ name: Bob, age: 25 }, { name: Charlie, age: 35 }]这种按字段投影比较的写法在处理来自不同数据源、字段结构不一致的对象例如合并来自两个 API 的用户列表时非常实用。你可以进一步组合条件例如(a, b) a.name b.name a.dept b.dept实现多字段联合判等。源码实现剖析differenceWith在仓库中的实现非常简洁完整逻辑位于 src/array/differenceWith.tsexport function differenceWithT, U( firstArr: readonly T[], secondArr: readonly U[], areItemsEqual: (x: T, y: U) boolean ): T[] { return firstArr.filter(firstItem { return secondArr.every(secondItem { return !areItemsEqual(firstItem, secondItem); }); }); }其算法可以拆解为两层外层filter遍历firstArr的每个元素决定是否保留内层every对当前firstItem逐一与secondArr中的元素调用areItemsEqual比较只有当secondArr中没有任何元素与它相等即areItemsEqual全部返回false时才保留该元素。这种filter every的组合与存在则排除的语义一一对应secondArr.every(...)检查第二数组中没有能匹配上它的元素取反后等价于它在第二数组中不存在。由于每次比较都走自定义回调该实现天然支持上面提到的跨类型比较与复杂业务规则。需要留意的是时间复杂度differenceWith是O(n × m)的双重循环n 为firstArr长度m 为secondArr长度因为它无法像difference那样借助Set做哈希查找自定义比较函数不具备可哈希性。对于小规模数组这完全够用如果数据量很大建议优先考虑在比较逻辑允许时用difference或先对数据做规范化处理。与 difference 的对比何时选谁es-toolkit 还提供了基于SameValueZero严格比较的 difference。它的实现在 src/array/difference.tsexport function differenceT(firstArr: readonly T[], secondArr: readonly T[]): T[] { const secondSet new Set(secondArr); return firstArr.filter(item !secondSet.has(item)); }两者的取舍非常清晰difference要求两个数组元素类型相同使用严格相等语义Set的has即SameValueZero时间复杂度 O(n m)适合数字、字符串等基本类型的快速差集differenceWith牺牲Set的常数级查找O(n × m)换取自定义比较能力适合对象数组按字段、按模糊规则判等的场景。一个直观的对照difference([1, 2, 3], [2])返回[1, 3]而当你需要difference([{ id: 1 }, { id: 2 }], [{ id: 2 }])这种按id判等时就必须使用differenceWith。如果比较规则恰好是基本类型完全相等直接使用difference会获得更好的性能。单元测试如何验证行为仓库的测试文件 src/array/differenceWith.spec.ts 覆盖了几个关键行为可作为使用时的行为契约参考自定义比较生效differenceWith([1.2, 2.3, 3.4], [1.2], (x, y) Math.floor(x) Math.floor(y))返回[2.3, 3.4]——浮点数按取整后是否相等判断这是difference无法做到的跨类型比较CSV[]与JSON[]两种不同接口类型的数组按id求差集验证了泛型签名T/U的可用性重复元素处理differenceWith([1, 1, 2, 2, 3], [2], (a, b) a b)返回[1, 1, 3]——与difference一致重复元素按逐个保留不匹配项处理不去重、不塌缩。扩展lodash 兼容版 differenceWith如果你正在从 lodash 迁移es-toolkit 在es-toolkit/compat下提供了 lodash 兼容版的differenceWith文档见 docs/compat/reference/array/differenceWith.md实现见 src/compat/array/differenceWith.ts。import { differenceWith } from es-toolkit/compat; // 按 id 比较 const objects [{ id: 1 }, { id: 2 }, { id: 3 }]; const others [{ id: 2 }]; const comparator (a, b) a.id b.id; differenceWith(objects, others, comparator); // 返回: [{ id: 1 }, { id: 3 }] // 同时从多个数组中排除 const array [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; const values1 [{ id: 2 }]; const values2 [{ id: 3 }]; differenceWith(array, values1, values2, comparator); // 返回: [{ id: 1 }, { id: 4 }] // 不传比较函数时行为退化为普通 difference differenceWith([1, 2, 3], [2], [3]); // 返回: [1]兼容版与标准版的主要差异可变参数签名是differenceWith(array, ...values, comparator)支持一次传入多个排除数组最后一个参数如果是函数则作为比较器comparator否则回退为普通difference逻辑输入宽容array参数接受ArrayLikeT | null | undefined非类数组输入如null、数字会直接返回[]其余values也会被flattenArrayLike扁平化处理并忽略非类数组的值这些行为在 src/compat/array/differenceWith.spec.ts 中都有对应测试lodash 语义对齐无比较器时内部复用difference并调用normalizeZero将-0规范化为0同时NaN能被正确匹配排除differenceWith([1, NaN, 3], [NaN])返回[1, 3]。官方兼容文档也给出了一个明确的性能提示兼容版为了处理null/undefined、多数组与ArrayLike类型运行速度比标准版慢因此在全新代码中建议优先使用es-toolkit/array下的标准differenceWith。使用注意事项小结比较函数应当是纯函数areItemsEqual会在内层every中被反复调用最多 n × m 次应避免在其中做副作用或昂贵计算比较规则需要保持确定性否则结果不可预测。返回的是新数组filter不会修改firstArr原数组原数据保持不变。重复元素按出现次数保留结果不去重若需要去重后的差集可结合uniq使用。大数组慎用O(n × m) 的复杂度意味着两个各 1 万元素的数组需要亿次级比较此时应优先考虑能否改用difference或先做索引映射。differenceWith以极简的实现核心仅 6 行代码填补了difference与复杂业务判等之间的空白是处理对象差集、跨源数据对比时的首选工具。【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考