news 2026/9/17 1:53:22

es-toolkit/fp isSubset 详解:用 pipe 组合判断数组子集关系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
es-toolkit/fp isSubset 详解:用 pipe 组合判断数组子集关系

es-toolkit/fp isSubset 详解:用 pipe 组合判断数组子集关系

【免费下载链接】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

本篇技术指南围绕 es-toolkit 函数式编程入口es-toolkit/fp中的isSubset展开:它负责创建"检查被 pipe 的数组是否为另一个数组子集"的谓词函数,专门用于数据流式管道组合。读完本文,你将掌握 fp 版isSubset的调用签名、与普通版isSubset的取舍、底层实现原理(基于differenceSet的 O(n) 判定),以及如何结合pipe实现可读性更高的链式数据校验。

一、函数概览:面向 pipe 的子集判定谓词

isSubset是 es-toolkit fp(函数式编程)子模块提供的一个高阶函数:它并不直接接收待检查的数组,而是先接收superset(超集),返回一个"判定函数",再由该判定函数接收被 pipe 传入的数组并返回布尔值。这一设计使其天然适配 fp 模块的pipe管道风格:

const result = pipe(array, isSubset(superset));

对比普通命令式写法,管道风格把"数据"与"变换"分离:数据从左向右流动,isSubset(superset)作为已配置好的操作符接收上游数据,返回true/false

普通版与 fp 版的选择

官方文档明确给出了使用建议:

  • 普通代码(未使用管道组合)中,推荐使用 es-toolkit 主入口的isSubset(从es-toolkit/array导入),它直接接收(superset, subset)两个数组参数;
  • 使用pipe串联变换时,应使用本页的 fp 版isSubset(从es-toolkit/fp导入)。

两种风格解决的是同一语义,只是参数形态不同:fp 版本质上是普通版的"数据后置(data-last)"柯里化封装。

二、使用方式

isSubset在 pipe 管道中的判定规则是:当被 pipe 数组中的每一个值都出现在superset中时返回true

import { isSubset, pipe } from 'es-toolkit/fp'; pipe([1, 2], isSubset([1, 2, 3])); // => true pipe([1, 4], isSubset([1, 2, 3])); // => false

第一个示例中[1, 2]的两个元素都在[1, 2, 3]中,判定为true;第二个示例中4不在超集中,判定为false

参数说明

参数类型说明
supersetreadonly T[]可能包含被 pipe 数组全部值的数组(超集)

返回值

返回类型为(array: readonly T[]) => boolean:一个把readonly T[]映射为"是否为子集"布尔值的函数。也就是说,isSubset(superset)本身不执行判定,只有把数据传入返回的函数(通常由pipe完成)后才产生结果。

三、源码级实现原理

3.1 fp 层的柯里化封装

fp 版实现位于 src/fp/array/isSubset.ts,其核心是一个闭包:先捕获superset,返回一个接收array的判定函数,内部委托给主库的isSubset

export function isSubset<T>(superset: readonly T[]): (array: readonly T[]) => boolean { return function (array: readonly T[]): boolean { return isSubsetToolkit(superset, array); }; }

注意参数顺序的变换:主库签名是isSubset(superset, subset),而 fp 版把第一个参数superset提前配置,将subset延后到数据流中传入。这种"配置参数先行、数据参数后置"的模式贯穿整个es-toolkit/fp模块。

3.2 主库的判定逻辑:difference + Set

真正执行判定的主库实现位于 src/array/isSubset.ts:

export function isSubset<T>(superset: readonly T[], subset: readonly T[]): boolean { return difference(subset, superset).length === 0; }

其思路是:计算subset相对superset的差集(即在subset中存在但superset中不存在的元素),若差集为空,则说明subset的所有元素都被superset包含,即子集成立。

而差集本身由 src/array/difference.ts 实现,其关键优化在于先将第二个数组(超集)转换为Set,使成员判断达到 O(1)

export function difference<T>(firstArr: readonly T[], secondArr: readonly T[]): T[] { const secondSet = new Set(secondArr); return firstArr.filter(item => !secondSet.has(item)); }

由此,整体判定复杂度为 O(n + m)(n 为 subset 长度、m 为 superset 长度),避免了朴素实现中 O(n×m) 的双重循环,在大数组场景下性能优势明显。

3.3 导出入口

fp 版isSubset通过 src/fp/array/index.ts 导出,与isSubsetWith并列,统一从es-toolkit/fp提供:

export { isSubset } from './isSubset.ts'; export { isSubsetWith } from './isSubsetWith.ts';

四、在 pipe 管道中的实际组合场景

pipees-toolkit/fp的入口,其实现位于 src/fp/pipe.ts。它按从左到右的顺序将值依次穿过每个函数,并针对连续出现的惰性函数(mapfiltertake等)做了融合优化,逐元素处理、支持提前短路。

isSubset属于非惰性谓词操作符,可在管道末尾作为"校验阀门"使用。例如先对数据进行变换,再判断结果是否落在许可范围内:

import { pipe, map, isSubset } from 'es-toolkit/fp'; const allowed = [2, 4, 6, 8, 10]; // 将输入放大后校验其是否全部属于 allowed const allEvenWithinRange = pipe( [1, 2, 3, 4, 5], map(x => x * 2), isSubset(allowed) ); // => true

在权限、特性开关、标签等"校验一组值是否都在允许列表内"的场景中,这种管道写法比嵌套调用更易读、易维护。

边界情况的判定行为

虽然 fp 版文档聚焦管道用法,其底层的子集语义继承自主库isSubset,因此以下边界行为同样成立:

  • 空数组恒为子集pipe([], isSubset([1, 2, 3]))返回true,因为差集为空;
  • 同一数组是其自身的子集pipe([1, 2, 3], isSubset([1, 2, 3]))返回true
  • 重复元素也能正确处理pipe([2, 2], isSubset([1, 2, 2, 3]))返回true(Set 判定基于值的存在性)。

五、自定义相等判定的扩展:isSubsetWith

当数组元素是对象、且需要按特定字段比较相等性时,fp 模块还提供了带自定义比较函数的isSubsetWith(实现见 src/fp/array/isSubsetWith.ts,底层委托 src/array/isSubsetWith.ts):

import { isSubsetWith, pipe } from 'es-toolkit/fp'; pipe( [{ id: 1 }], isSubsetWith([{ id: 1 }, { id: 2 }], (a, b) => a.id === b.id) ); // => true

其签名在isSubset基础上追加了第三个配置参数areItemsEqual: (item: T, other: T) => boolean,判定时改用该函数逐一比较元素(内部基于differenceWith实现),适用于引用类型或需要忽略部分字段的比对场景。

六、测试验证

仓库为 fp 版isSubset提供了单元测试 src/fp/array/isSubset.spec.ts,使用 Vitest 验证其在管道中的行为:

import { describe, expect, it } from 'vitest'; import { isSubset } from './isSubset.ts'; import { pipe } from '../pipe.ts'; describe('isSubset', () => { it('works in a pipe', () => { expect(pipe([1, 2], isSubset([1, 2, 3]))).toEqual(true); }); });

测试用例与文档示例保持一致,可直接作为回归基准。更完整的子集语义(空数组、自包含、重复元素等边界)可在主库对应测试中进一步查阅。

七、小结

  • fp 版isSubset是一个"先配置超集、再判定输入"的高阶函数,专为pipe管道设计,从es-toolkit/fp导入;
  • 底层判定委托主库isSubset,通过difference(subset, superset).length === 0实现,差集计算借助Set达到 O(n + m) 复杂度;
  • 普通命令式代码应直接使用es-toolkit/arrayisSubset;只有用pipe组合变换时才切换到 fp 版;
  • 需要自定义元素相等性时,使用 fp 版isSubsetWith

掌握 fp 版isSubset后,你可以在 es-toolkit 的函数式管道中优雅地实现"结果校验"与"许可范围检查",让数据流式处理的最后一道关卡同样保持声明式、可组合的风格。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 1:53:13

15分钟导出微信聊天记录并永久保存:WeChatMsg快速上手教程

15分钟导出微信聊天记录并永久保存&#xff1a;WeChatMsg快速上手教程 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/W…

作者头像 李华
网站建设 2026/9/17 1:53:05

2026年Docker部署实战:从AI大模型到数据库的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 1:52:55

基于51单片机交通灯设计:状态机与定时器中断实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 1:51:02

AI代码生成工具怎么评估?四个维度与真实场景实测指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 1:48:29

CMake接口库(INTERFACE)实战:跨项目依赖传递与编译配置复用

做 C/C 项目的人&#xff0c;多多少少都要跟 CMake 打交道。CMake 里最被低估的一个特性&#xff0c;我认为是add_library的INTERFACE选项&#xff0c;也就是所谓的接口库&#xff08;interface library&#xff09;。它不编译任何源文件&#xff0c;却能统一声明头文件路径、编…

作者头像 李华
网站建设 2026/9/17 1:48:24

npm 版本列表查看全攻略:从 npm view 到 semver 范围解析

最近又被版本问题折腾了一回&#xff1a;项目里 package.json 清清楚楚写着"vue": "^2.6.14"&#xff0c;本地装出来好好的&#xff0c;同事一拉代码直接变成了 2.7.x&#xff0c;页面样式乱七八糟。查来查去发现&#xff0c;^2.6.14这个范围在 npm 眼里本…

作者头像 李华