es-toolkit 的 isSubsetWith:使用自定义比较函数判断数组子集关系
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
isSubsetWith是 es-toolkit 数组模块中用于判断「一个数组是否为另一个数组的子集」的函数,与基于严格相等(===)的isSubset不同,它允许你传入自定义比较函数,从而支持对象按字段比较、大小写不敏感字符串比较、数值容差比较等复杂场景。读完本文,你将掌握isSubsetWith的完整签名、参数语义、典型实战用法、底层实现原理与边界行为,并能将其应用到权限校验、数据清洗、配置校验等真实业务中。
函数签名与参数语义
isSubsetWith的调用形式为:
const result = isSubsetWith(superset, subset, areItemsEqual);它接收三个参数:
superset(readonly T[]):上位集合数组,理论上应包含子集的全部元素;subset(readonly T[]):待验证的子集数组,检查其中的每个元素是否都能在上位集合中找到「相等」的元素;areItemsEqual((x: T, y: T) => boolean):用户自定义的比较函数,用于判断两个元素是否相等,相等返回true,否则返回false。
返回值(boolean):以用户定义的比较函数为基准,当subset中的每一个元素都能在superset中找到匹配元素时返回true,否则返回false。
使用方式为按需导入:
import { isSubsetWith } from 'es-toolkit/array';该函数在 src/array/index.ts 中统一导出,与isSubset、difference、intersection等数组函数并列,因此也可以从es-toolkit的数组入口整体引入。
典型使用场景
按对象 id 判断子集关系
当比较对象数组时,直接使用===会因引用不同而失败,此时按业务主键(如id)比较是最常见的做法:
import { isSubsetWith } from 'es-toolkit/array'; // 按对象的 id 判断子集 const users = [ { id: 1, name: 'john' }, { id: 2, name: 'jane' }, { id: 3, name: 'bob' }, ]; const targetUsers = [ { id: 2, name: 'jane' }, { id: 1, name: 'john' }, ]; isSubsetWith(users, targetUsers, (a, b) => a.id === b.id); // 返回: true // 当并非子集时 const allUsers = [ { id: 1, name: 'john' }, { id: 2, name: 'jane' }, ]; const someUsers = [{ id: 3, name: 'bob' }]; isSubsetWith(allUsers, someUsers, (a, b) => a.id === b.id); // 返回: false可以看到,targetUsers中的元素顺序与users不一致,但这不影响判断——子集关系本身与顺序无关。
大小写不敏感的比较
通过自定义比较函数,可以让字符串比较忽略大小写差异:
import { isSubsetWith } from 'es-toolkit/array'; // 大小写不敏感的字符串比较 const validNames = ['Alice', 'Bob', 'Charlie']; const userNames = ['alice', 'BOB']; isSubsetWith(validNames, userNames, (a, b) => a.toLowerCase() === b.toLowerCase()); // 返回: true数值容差范围内的比较
对于浮点数或测量值,精确相等往往不适用,可以基于误差范围判断:
import { isSubsetWith } from 'es-toolkit/array'; // 数值范围比较 const validRanges = [1, 2, 3, 4, 5]; const testNumbers = [1.1, 2.8]; isSubsetWith(validRanges, testNumbers, (a, b) => Math.abs(a - b) < 0.5); // 返回: true(1.1 与 1 足够接近,2.8 与 3 足够接近)底层实现原理
从源码层面看,isSubsetWith的实现极为精简,它直接复用了另一个数组函数differenceWith:
// src/array/isSubsetWith.ts import { differenceWith } from './differenceWith.ts'; export function isSubsetWith<T>( superset: readonly T[], subset: readonly T[], areItemsEqual: (x: T, y: T) => boolean ): boolean { return differenceWith(subset, superset, areItemsEqual).length === 0; }实现逻辑可以概括为:先求出「子集中存在、但上位集合中不存在的元素」的差集,若差集为空,则子集必然完全包含于上位集合。这与普通版本isSubset的设计一脉相承——src/array/isSubset.ts 同样是基于difference(subset, superset).length === 0实现的,区别仅在于比较策略:isSubset使用严格的===,而isSubsetWith把比较逻辑交给用户自定义。
而differenceWith本身(src/array/differenceWith.ts)采用双层遍历实现:
export function differenceWith<T, 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); }); }); }即对子集的每个元素,检查上位集合中是否全部元素都与之不相等;只要存在一个相等元素,该子集元素就被保留(进入差集)。因此isSubsetWith的时间复杂度为 O(n × m)(n 为子集长度,m 为上位集合长度),且比较函数会被频繁调用。对于超大规模数组,需要考虑这一开销,或改用预索引等优化手段。
边界行为与测试验证
es-toolkit 为isSubsetWith提供了完善的单元测试(src/array/isSubsetWith.spec.ts),可以从中确认几个关键边界行为:
- 空子集:
isSubsetWith([1, 2, 3], [], (a, b) => a === b)返回true——数学上空集是任何集合的子集; - 空上位集合:
isSubsetWith([], [1], ...)返回false——空集合不可能包含非空元素; - 重复元素:
isSubsetWith([1, 2, 2, 3], [2, 2], ...)返回true,而isSubsetWith([1, 2, 2, 3], [2, 2, 4], ...)返回false——重复元素按出现次数逐一匹配,多余的需求(如4)会使结果变为false。
理解这些边界行为,能帮助你在权限校验、配置比对等场景中避免直觉性误判。
函数式(FP)变体
除了普通版本,es-toolkit 还提供了函数式编程风格的isSubsetWith(src/fp/array/isSubsetWith.ts),它采用柯里化(curried)签名,适合与pipe组合使用:
import { isSubsetWith, pipe } from 'es-toolkit/fp'; pipe( [{ id: 1 }], isSubsetWith([{ id: 1 }, { id: 2 }], (a, b) => a.id === b.id) ); // => true其实现本质是对普通版本的薄封装:先接收superset与areItemsEqual,返回一个「待验证数组 → 布尔值」的谓词函数,再在内部调用普通版本完成判断。相关测试见 src/fp/array/isSubsetWith.spec.ts,确认了它在pipe数据流中的可用性。
与相关函数的选型对比
| 函数 | 比较方式 | 适用场景 |
|---|---|---|
isSubset | 严格=== | 基础类型数组(数字、字符串)的子集判断,性能好、语义简单 |
isSubsetWith | 用户自定义函数 | 对象按字段比较、大小写不敏感、数值容差等复杂相等性判断 |
differenceWith | 用户自定义函数 | 需要拿到差集元素本身,而不只是布尔结论时 |
intersection | 严格=== | 需要求出两个数组的交集元素 |
选型建议:当元素是基本类型且无特殊比较需求时,优先使用isSubset;当涉及对象、浮点数或模糊匹配时,isSubsetWith是更合适的选择;如果你需要的不只是「是否为子集」的布尔结论,而是具体的差异元素,可直接使用differenceWith。
小结
isSubsetWith以「差集为空则子集成立」这一简洁思路,将子集判断与自定义相等性逻辑解耦,使开发者可以在保持 API 极简的同时覆盖从对象主键比对到模糊数值匹配的各类业务场景。结合 es-toolkit 统一的es-toolkit/array导入方式、完善的边界测试以及函数式变体,它是构建数据校验、权限过滤等逻辑时一个可靠且可组合的基础工具。
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考