 成员查找)
Sanity 仓库实践用 Set/Map 替代 includes 实现 O(1) 成员查找【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity本文围绕 Sanity 仓库内置的 Vercel React 最佳实践规则js-set-map-lookups展开说明为何在循环或高频路径中反复使用Array.includes()做成员判断会产生 O(n×m) 的复合复杂度如何将其转换为Set/Map的 O(1) 查找并结合 Sanity Studio 源码中的真实用例文档选择状态机、配置预处理验证该模式在大型 React 工程中的落地方式。读完后你可以掌握判断何时值得转换、如何安全转换、以及该规则在仓库代码审查中的定位。规则定位JavaScript Performance 类别的 LOW-MEDIUM 优化项该规则来自仓库中的 js-set-map-lookups.md是 vercel-react-best-practices 技能 中「JavaScript PerformanceJavaScript 性能」类别下的第 7.11 条。按 SKILL.md 中的分类整个技能包含 57 条规则、8 个优先级类别优先级类别影响级别前缀1Eliminating WaterfallsCRITICALasync-2Bundle Size OptimizationCRITICALbundle-3Server-Side PerformanceHIGHserver-4Client-Side Data FetchingMEDIUM-HIGHclient-5Re-render OptimizationMEDIUMrerender-6Rendering PerformanceMEDIUMrendering-7JavaScript PerformanceLOW-MEDIUMjs-8Advanced PatternsLOWadvanced-规则文件 frontmatter 中声明的元数据如下title: Use Set/Map for O(1) Lookups impact: LOW-MEDIUM impactDescription: O(n) to O(1) tags: javascript, set, map,>const allowedIds [a, b, c, ...] items.filter(item allowedIds.includes(item.id))Array.prototype.includes()每次调用都要从头扫描数组时间复杂度为 O(n)。当它出现在filter/map等回调中时总复杂度退化为 O(外部长度的 m × 内层数组长度 n)。例如 10,000 条记录对 1,000 个白名单 ID 做过滤就是约一千万次比较。正确写法每次检查 O(1)const allowedIds new Set([a, b, c, ...]) items.filter(item allowedIds.has(item.id))Set底层基于哈希表has()平均时间复杂度为 O(1)。代价是一次性的 O(n) 构建成本但只要成员检查发生多次尤其是遍历场景总复杂度从 O(m×n) 降到 O(mn)。判断标准可以概括为一条当同一个数组/白名单被重复查询循环内、渲染内、事件高频触发时就该换成Set如果只是单次检查一个小数组includes的常数开销反而更小不需要为此付出构建Set的成本。姊妹规则用 Map 建立键值索引同一类别下的 js-index-maps.mdBuild Index Maps for Repeated Lookups是Set模式的自然延伸当多次.find()都按同一个键查找键 → 值的对应关系时应改用Map。其 frontmatter 给出的量化例子是1M ops to 2K ops// 错误每次查找 O(n) function processOrders(orders: Order[], users: User[]) { return orders.map((order) ({ ...order, user: users.find((u) u.id order.userId), })) }// 正确每次查找 O(1) function processOrders(orders: Order[], users: User[]) { const userById new Map(users.map((u) [u.id, u])) return orders.map((order) ({ ...order, user: userById.get(order.userId), })) }规则文件中的结论是Map 只构建一次O(n)之后所有查找都是 O(1)以 1000 个订单 × 1000 个用户为例总操作量从约 100 万次降到约 2000 次。两条规则的分工可以总结为只需要判断存在与否白名单、去重、已处理标记→Set需要按键取回对象/值ID → 实体、类型 → 元数据→MapSanity Studio 源码中的真实用例Sanity 仓库本体就是这条规则的最佳注脚。在packages/sanity/src/core目录下new Set(...)/new Map(...)配合.has()的写法出现在数十个文件中。挑两个有代表性的用例一文档批量选择状态机中的白名单裁剪。selectionMachine.ts 中pruneSelectedIds函数负责把用户已选中的文档 ID 集合裁剪到仍然存在于当前 variant 列表的子集内——这正是规则文档中items.filter(item allowedIds.has(item.id))的教科书形态function pruneSelectedIds(selectedIds: Setstring, variants: Variant[]): Setstring { const availableIds new Set(variants.map((variant) variant.id)) return new Set([...selectedIds].filter((id) availableIds.has(id))) }这里selectedIds本身就是Setstring状态机的assign({selectedIds: () new Setstring()})初始化availableIds由 variant 列表构建。批量选择/发布场景中 variant 列表可能有成百上千项每次选择变更都要做一遍裁剪用Set.has避免了 O(选中数 × variant 数) 的退化。同目录下的 computeSets.ts 也是围绕 Set 运算并集/交集/差集做集合计算的工具。用例二配置预处理中的工作区去重缓存。prepareConfig.tsx 在把用户配置展开为WorkspaceSummary时用Map做同一 workspace 定义只预处理一次的去重const workspaces workspaceOptions.map((rawWorkspace): WorkspaceSummary { if (preparedWorkspaces.has(rawWorkspace)) { return preparedWorkspaces.get(rawWorkspace)! } // ...执行完整预处理 })has()先探测、get()再取值命中时直接返回缓存结果——这正是js-index-maps规则的构建一次、查找 O(1)模式在配置解析路径上的应用。其他佐证findMovesInArrayOfObjects.ts 用intersectingKeys.has(_key)在移动检测中做键集合的成员判断findMostSpecificTarget.ts 用values.has(exactId)做 change indicator 的目标匹配。可以推断这些高频运行于表单/文档编辑路径上的工具函数正是LOW-MEDIUM 影响级别、但数据规模上来后收益显著的典型场景。落地建议与注意事项结合规则文档与仓库实践给出可直接执行的检查清单循环/遍历中的arr.includes(x)是首要排查目标.filter、.map、.some、.every回调里对另一个数组做includes/find先问内层数组在循环期间是否不变——不变就提升到循环外构建Set/Map。React 中的构建时机Set/Map本身有构建成本。若数据源是 props/state 且较大建议在useMemo中构建一次而不是在组件函数体内每次渲染都new Set(...)——这与同技能中rerender-*系列如 rerender-memo.md的把昂贵计算移出渲染路径原则一致。区分 Set 与 Map 的语义Set只能回答在不在Map能顺带取回值。若后续逻辑还需要该键对应的对象如 ID → 文档元数据直接用Map避免先Set.has再arr.find的两段式浪费。不要过度优化一次性、小数组几个到几十个元素的成员检查includes完全够用这条规则的价值随内层数组长度 × 检查次数的乘积放大。与相邻规则的配合如果同一循环里既要做成员判断又要取对象优先落到js-index-maps的Map方案如果只是去重还可以直接new Set(arr)一步完成这与js-combine-iterations合并多次遍历等同类规则可以叠加使用。小结js-set-map-lookups规则的核心主张一句话概括对不变的数组做重复成员查询时用 O(n) 的一次性构建换取 O(1) 的每次查找。它影响级别虽标为 LOW-MEDIUM但复杂度改善是从乘法级O(m×n)到加法级O(mn)的质变。Sanity 仓库中状态机选择裁剪、配置预处理去重等处的真实写法验证了这一模式在大型 React 内容工作区工程中的普遍性与必要性——这也是 SKILL.md 将其纳入代码审查与自动化重构规则集的原因。【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考