)
es-toolkit 的 differenceWith 详解用自定义比较函数计算数组差集Lodash 兼容版与高性能版对比【免费下载链接】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 中用于「按自定义比较逻辑求数组差集」的工具函数它在es-toolkit/compat兼容入口中完整复刻了 Lodash 的行为不仅支持多个排除数组、ArrayLike与null/undefined容错还能在省略比较函数时自动退化为普通difference语义。读完本文你将掌握differenceWith的完整用法、参数约定、与 Lodash 的兼容细节如-0/NaN处理并能通过源码与测试用例理解 compat 版为何比现代版更重、以及在什么场景下应该改用 es-toolkit 原生版。一、函数签名与核心语义differenceWith的作用是使用一个自定义比较函数comparator来判断两个元素是否相等然后从第一个数组中剔除所有在其余数组中出现过的元素返回一个新的数组。其 TypeScript 签名compat 版如下differenceWith(array, ...values, comparator);arrayArrayLikeT | null | undefined求差集的基准数组。...valuesArrayArrayLikeT(a: T, b: T) boolean一个或多个需要排除的数组其中最后一个参数是比较函数。返回值T[]按比较函数剔除后的新数组原数组不会被修改。与 Lodash 一致比较函数必须放在最后一个参数位置如果省略比较函数函数行为与普通difference完全相同。二、基础用法按对象字段求差集最常见的场景是对象数组之间按某个字段如id比较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); // Returns: [{ id: 1 }, { id: 3 }]这里{ id: 2 }与排除数组中的{ id: 2 }经比较函数判定相等因此被剔除其余元素保留。由于比较逻辑完全由你控制它也能比较不同类型的数组例如对象与数字// 对象 id 与数字直接比较对应现代版 es-toolkit 的用法 import { differenceWith } from es-toolkit/array; const objects [{ id: 1 }, { id: 2 }, { id: 3 }]; const numbers [2, 4]; const areItemsEqual (a, b) a.id b; differenceWith(objects, numbers, areItemsEqual); // Returns: [{ id: 1 }, { id: 3 }]三、多数组一次排除differenceWith支持同时传入多个排除数组元素只要与其中任意一个数组中的元素匹配即被剔除import { differenceWith } from es-toolkit/compat; const array [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; const values1 [{ id: 2 }]; const values2 [{ id: 3 }]; differenceWith(array, values1, values2, comparator); // Returns: [{ id: 1 }, { id: 4 }]从源码看compat 实现会对...values做一次扁平化合并再统一与基准数组比较见 flattenArrayLike// src/compat/array/differenceWith.ts核心逻辑节选 const comparator last(values); const flattenedValues flattenArrayLike(values as ArrayArrayLikeT); if (typeof comparator function) { return differenceWithToolkit(Array.from(array), flattenedValues, comparator); } return differenceToolkit(Array.from(array), flattenedValues).map(normalizeZero);也就是说先取出最后一个参数判断是否为函数若为函数则作为比较器否则整批参数都按数组处理。flattenArrayLike会跳过其中不是ArrayLike的值isArrayLikeObject判断这也是 compat 版比原生版多出的容错成本之一。四、省略比较函数退化为普通 difference当最后一个参数不是函数例如全是数组时differenceWith会回退到基于SameValueZero算法的普通差集计算效果等同于differenceimport { differenceWith } from es-toolkit/compat; // 不使用比较函数时执行普通比较 differenceWith([1, 2, 3], [2], [3]); // Returns: [1]这条回退路径在源码中对应return differenceToolkit(Array.from(array), flattenedValues).map(normalizeZero);其中differenceToolkit是 src/array/difference.ts 中的现代实现内部借助Set完成 O(n) 级别的去重判断。注意末尾的.map(normalizeZero)这是为了对齐 Lodash 行为把结果中的-0规范化为0见 normalizeZero。五、复杂比较逻辑自定义相等规则比较函数完全由你定义因此可以实现「只看部分字段」这类复杂规则。例如只按name判断用户是否相同即使年龄不同也会被排除import { differenceWith } from es-toolkit/compat; const users [ { name: alice, age: 25 }, { name: bob, age: 30 }, { name: charlie, age: 35 }, ]; const excludeUsers [{ name: bob, age: 25 }]; // 年龄不同 // 只按名字比较 const compareByName (a, b) a.name b.name; differenceWith(users, excludeUsers, compareByName); // Returns: [{ name: alice, age: 25 }, { name: charlie, age: 35 }] // bob 被排除年龄不同但名字相同现代版 docs/reference/array/differenceWith.md 也演示了同样的能力两个对象数组即使age不同只要name相同即视为同一用户。比较函数同样支持直接引入isEqual做深比较import { differenceWith } from es-toolkit/compat; import { isEqual } from es-toolkit; const objects [ { x: 1, y: 2 }, { x: 2, y: 1 }, ]; differenceWith(objects, [{ x: 1, y: 2 }], isEqual); // Returns: [{ x: 2, y: 1 }]六、边界行为与测试验证compat 版的细节行为均有对应的测试用例保障见 src/compat/array/differenceWith.spec.ts测试覆盖了 Lodash 官方用例并补充了更多边界-0与0互相匹配结果归一化为0differenceWith([-0, 1], [1])返回[0][-0, 0]与[0]求差集得到[]。NaN匹配differenceWith([1, NaN, 3], [NaN, 5, NaN])返回[1, 3]说明走SameValueZero语义时NaN等于自身。大数组测试使用LARGE_ARRAY_SIZE构造大规模输入验证-0、NaN、对象引用在大量数据下行为一致differenceWith([-0, 1], largeArray)仍返回[0]。忽略非 ArrayLike 参数differenceWith(null, array, 1)返回[]differenceWith(array, args, null)跳过非法参数基准数组不是ArrayLike时直接返回空数组对应源码开头的if (!isArrayLikeObject(array)) return [];。显式 comparator 时保留0的符号使用eq作为比较函数时-0会按原样保留[-0]因为normalizeZero只作用于无比较函数的回退路径。七、compat 版与原生版如何选择官方文档在 compat 参考页顶部给出了明确建议见 docs/compat/reference/array/differenceWith.md 及日文版 docs/ja/compat/reference/array/differenceWith.md 的警告框这个differenceWith函数因为要处理null/undefined、多数组处理以及ArrayLike类型处理运行会变慢。建议改用更快、更现代的 es-toolkit 原生differenceWith。两者差异一览对比维度es-toolkit/compat版es-toolkit/array原生版入口import { differenceWith } from es-toolkit/compatimport { differenceWith } from es-toolkit/array签名differenceWith(array, ...values, comparator)differenceWith(firstArr, secondArr, areItemsEqual)多数组支持内部先flattenArrayLike合并仅两个数组空值容错null/undefined/非ArrayLike自动处理要求真实数组默认比较SameValueZero-0归一化normalizeZero基于Set的严格相等实现src/compat/array/differenceWith.tssrc/array/differenceWith.ts原生版的核心实现非常精简直接对两个数组做过滤// src/array/differenceWith.ts return firstArr.filter(firstItem { return secondArr.every(secondItem { return !areItemsEqual(firstItem, secondItem); }); });因此如果是新项目且只需在两个数组间做自定义比较应优先使用es-toolkit/array的原生differenceWith只有当需要从 Lodash 迁移、依赖多数组与宽松参数容错时才使用es-toolkit/compat入口该入口在 src/compat/compat.ts 中统一导出。八、小结differenceWith是处理「非严格相等」差集需求的利器通过一个比较函数就能按对象字段、部分属性乃至任意自定义规则求差集。compat 版在保留 Lodash 全部兼容行为多数组、空值容错、-0/NaN语义的同时与现代版共享同一套核心算法src/array/differenceWith.ts并借助Set实现的高效difference与normalizeZero保证结果与 Lodash 一致。结合 测试用例 中覆盖的边界场景你可以放心地在迁移 Lodash 代码或处理复杂对象比较时使用它。【免费下载链接】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),仅供参考