news 2026/9/17 2:29:10

es-toolkit/fp 的 intersectionWith:用自定义比较函数在 pipe 管道中做交集运算

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
es-toolkit/fp 的 intersectionWith:用自定义比较函数在 pipe 管道中做交集运算

es-toolkit/fp 的 intersectionWith:用自定义比较函数在 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提供了一种数据后置(data-last)的intersectionWith变体:先用自定义等值函数配置好比较逻辑,得到一个等待数据的函数,再放进pipe管道中,以自上而下的顺序处理数组。本文以 docs/ja/fp/reference/intersectionWith.md 为骨架,结合源码实现与测试用例,讲清它的参数语义、惰性求值原理、与普通版intersectionWith的差异,以及实际可复制的用法。

一、它解决什么问题

es-toolkit/array中的普通intersectionWith接受三个参数:intersectionWith(firstArr, secondArr, areItemsEqual)。而在函数式编程风格中,我们希望把"筛选两个数组的交集"当作管道中的一步,与其他变换(mapfiltertake等)串联起来。

于是es-toolkit/fp版的intersectionWith被设计成分两段调用:第一段接收secondArrayareItemsEqual(配置阶段),第二段接收被管道传入的数组(数据处理阶段):

const result = pipe(array, intersectionWith(secondArray, areItemsEqual));

它返回一个新函数:把输入的readonly T[]映射为"与secondArray中至少一个值满足areItemsEqual相等判定"的元素数组。

二、基本用法

pipe中,intersectionWith保留那些对secondArray至少一个值areItemsEqual返回true的管道数组元素:

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

这里areItemsEqualid属性比较对象,因此只有{ id: 2 }保留下来。该示例与 src/fp/array/intersectionWith.spec.ts 中的测试用例完全一致。

参数

  • secondArrayreadonly U[]):包含比较目标值的数组,作为"参照集合"。
  • areItemsEqual(item: T, other: U) => boolean):判断两个值是否相等的函数,返回true表示相等。

返回值

(array: readonly T[]) => T[]:一个把readonly T[]转换为"与比较函数匹配的值数组"的函数。也就是说,intersectionWith(secondArray, areItemsEqual)本身就是一个等待数据的一元函数,天然适配pipe

三、源码级解析:Eager + Lazy 双实现

从源码结构看,src/fp/array/intersectionWith.ts 同时准备了两套行为,并用内部工具组合它们:

export function intersectionWith<T, U>( secondArray: readonly U[], areItemsEqual: (item: T, other: U) => boolean ): (array: readonly T[]) => T[] { function intersectionWithEager(array: readonly T[]): T[] { return intersectionWithToolkit(array, secondArray, areItemsEqual); } const intersectionWithLazy = createLazyFunction<T, T>((value, _index, emit) => { if (secondArray.some(other => areItemsEqual(value, other))) { emit(value); } }); return combineEagerAndLazyFunctions(intersectionWithEager, intersectionWithLazy); }
  • Eager 实现:直接复用es-toolkit/arrayintersectionWith。其核心逻辑用filter+some表达——对firstArr的每个元素,检查secondArr中是否存在一个元素令areItemsEqual返回true

    return firstArr.filter(firstItem => { return secondArr.some(secondItem => { return areItemsEqual(firstItem, secondItem); }); });
  • Lazy 实现:用createLazyFunction构造一个逐元素(push 式)变换。对每个输入值,若secondArray中存在某个otherareItemsEqual(value, other)为真,就把该值emit给下一级管道。

  • 组合combineEagerAndLazyFunctions(见 src/fp/_internal/lazy.ts)把 eager 函数原样返回,并挂上lazy元数据。于是:直接调用时行为与普通版完全一致;放进pipe时,pipe可以识别其惰性变换并与其他惰性函数融合

四、惰性求值:为什么它适合放在管道中

pipe的融合机制(详见 docs/fp/intro.md 与 docs/fp/reference/pipe.md)把相邻的惰性函数(mapfiltertakeintersectionWith等)合并为单趟遍历:不再每步都产生中间数组,而是让每个元素一次性穿过所有阶段;当尾部的短路函数(如take)收集到足够结果时,整个遍历立即停止,后面的输入不再被访问。

intersectionWith的惰性变换在 src/fp/array/intersectionWith.spec.ts 中有明确的测试证据:

const spy = vi.fn((item: { id: number }) => item); expect( pipe( [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }], map(spy), intersectionWith([3], (item, id) => item.id === id), take(1) ) ).toEqual([{ id: 3 }]); expect(spy).toHaveBeenCalledTimes(3);

注意spy只被调用了3 次,而不是 4 次:take(1)收集到第一个结果({ id: 3 })后管道立即短路,{ id: 4 }根本不会被map处理。这正是"融合 + 短路"带来的收益——数据规模越大、take越早满足,节省的遍历就越多。

五、实战扩展:自定义比较函数的典型场景

借助自定义比较函数,intersectionWith可以处理普通===无法胜任的复杂对象。以下场景来自普通版intersectionWith文档,同样适用于fp版(只需把调用方式改为管道形式):

按对象属性比较

import { pipe } from 'es-toolkit/fp'; import { intersectionWith } from 'es-toolkit/fp'; const users1 = [ { id: 1, name: 'john' }, { id: 2, name: 'jane' }, ]; const users2 = [ { id: 2, name: 'jane' }, { id: 3, name: 'bob' }, ]; pipe(users1, intersectionWith(users2, (a, b) => a.id === b.id)); // => [{ id: 2, name: 'jane' }]

比较不同类型的值

泛型签名areItemsEqual: (item: T, other: U) => boolean允许两数组元素类型不同:

const objects = [ { id: 1, name: 'apple' }, { id: 2, name: 'banana' }, ]; const ids = [2, 3]; pipe(objects, intersectionWith(ids, (obj, id) => obj.id === id)); // => [{ id: 2, name: 'banana' }]

大小写不敏感的字符串比较

const words1 = ['Apple', 'Banana']; const words2 = ['apple', 'cherry']; pipe(words1, intersectionWith(words2, (a, b) => a.toLowerCase() === b.toLowerCase())); // => ['Apple']

数值容差比较

const numbers1 = [1.1, 2.3, 3.7]; const numbers2 = [1.0, 2.5, 4.0]; pipe(numbers1, intersectionWith(numbers2, (a, b) => Math.abs(a - b) < 0.5)); // => [1.1, 2.3, 3.7]

注意:返回结果始终来自被管道传入的第一个数组(保留其原始元素与顺序),secondArray只作为判定依据。

六、与普通版如何选择

es-toolkit/fp版并不是独立实现的另一套算法,而是对es-toolkit/array版的重封装,仅改变调用形态。选择建议如下:

  • 普通代码中直接调用:使用原版intersectionWith,即intersectionWith(firstArr, secondArr, areItemsEqual),更直观、无额外间接层。
  • 需要用pipe串联多个变换时:使用本fp版,把intersectionWith(secondArray, areItemsEqual)作为管道中的一步,享受惰性融合带来的单趟遍历与提前短路收益。
  • 迁移 Lodash 调用点:可参考es-toolkit/compat入口(见 docs/fp/intro.md 的说明)。

七、相关资源

  • 文档:docs/ja/fp/reference/intersectionWith.md、英文版 docs/fp/reference/intersectionWith.md、普通版 docs/reference/array/intersectionWith.md
  • 管道入口:docs/fp/reference/pipe.md,fp 模块总览:docs/fp/intro.md
  • 源码:src/fp/array/intersectionWith.ts、底层普通版 src/array/intersectionWith.ts、惰性求值原语 src/fp/_internal/lazy.ts
  • 测试:src/fp/array/intersectionWith.spec.ts

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

STM32CubeMX定时器配置避坑指南:从报错到稳定运行

/* 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 2:27:10

AI漫剧工业化流水线:一站式工作台如何实现量产与品控

1. 这不是“AI画画AI配音”的拼凑&#xff0c;而是一套真正能跑通的漫剧工业化流水线最近三个月&#xff0c;我陆陆续续测试了27个标榜“AI漫剧制作”的工具或平台&#xff0c;从开源项目到SaaS服务&#xff0c;从单机脚本到云端工作台&#xff0c;踩过的坑足够填满三本实操笔记…

作者头像 李华
网站建设 2026/9/17 2:23:46

uTools超级文本片段:跨应用实时模板,终结重复输入

每天打开电脑&#xff0c;总有一堆内容在不停重复&#xff1a;回复客户的第一句话、提交代码前的注释模板、报销单里的公司抬头、每周周报的开头格式。以前我桌面一直放着几个 txt 文件&#xff0c;里面存着各种常用话术&#xff0c;要用的时候打开复制粘贴&#xff1b;后来换成…

作者头像 李华
网站建设 2026/9/17 2:23:36

数据分析与科学计算:从NumPy到Pandas的完整实操指南

做数据分析这行有些年头了&#xff0c;从最早用 Excel 抠数据&#xff0c;到后来天天跟 Python、NumPy、Pandas 打交道&#xff0c;一个很深的感受是&#xff1a;真正难的不是某个函数怎么用、某张图怎么画&#xff0c;而是你拿到一堆杂乱数据时&#xff0c;脑子里的分析框架和…

作者头像 李华