尧图精选

es-toolkit 数组去重函数 uniq 全解析:基于 Set 的 O(n) 实现与边界行为

🕒 发布时间:2026/9/17 4:29:41 📁 来源:尧图网络
es-toolkit 数组去重函数 uniq 全解析基于 Set 的 O(n) 实现与边界行为【免费下载链接】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-toolkituniq 是 es-toolkit 提供的高性能数组去重函数它基于原生Set的 SameValueZero 相等语义在保持首次出现顺序的同时以 O(n) 时间复杂度完成去重。本文以官方参考文档 docs/ja/reference/array/uniq.md英文版见 docs/reference/array/uniq.md为主体结合源码与测试用例深入讲解 uniq 的用法、实现原理、边界行为及其与 uniqBy、uniqWith 的选型关系。uniq 是什么uniq 接收一个数组返回一个去除重复元素后的新数组。它与 lodash 的_.uniq功能一致但 es-toolkit 的现代实现更轻量、更快速是 lodash 的高性能替代方案。const uniqueArray uniq(arr);核心特性去重返回的新数组中每个元素只出现一次保序保留元素在原始数组中首次出现的顺序不修改原数组返回全新的数组输入数组保持不变O(n) 时间复杂度得益于内部使用Set整体复杂度为线性。使用方法uniq适用于从数组中移除重复值、只保留唯一值的场景它会保留原始数组中元素首次出现的顺序。import { uniq } from es-toolkit/array; // 从数字数组中去除重复项 const numbers [1, 2, 2, 3, 4, 4, 5]; const uniqueNumbers uniq(numbers); console.log(uniqueNumbers); // [1, 2, 3, 4, 5] // 从字符串数组中去除重复项 const words [apple, banana, apple, cherry, banana]; const uniqueWords uniq(words); console.log(uniqueWords); // [apple, banana, cherry] // 去除对象数组中引用相同的对象 const obj1 { id: 1 }; const obj2 { id: 2 }; const obj3 { id: 3 }; const objects [obj1, obj2, obj1, obj3, obj2]; const uniqueObjects uniq(objects); console.log(uniqueObjects); // [{ id: 1 }, { id: 2 }, { id: 3 }]注意对象数组的去重基于引用相等SameValueZeroobj1与另一个{ id: 1 }字面量即使结构相同也会被视为不同元素。如果希望按内容或字段去重应改用 uniqBy 或 uniqWith详见下文。空数组输入会返回空数组import { uniq } from es-toolkit/array; const emptyArray uniq([]); console.log(emptyArray); // []参数与返回值项目说明参数arrreadonly T[]要去重的数组返回值T[]去除重复后的新数组保留原始数组中首次出现的顺序由于参数类型为readonly T[]传入as const断言或ReadonlyArray类型的数组同样可以直接使用不会出现类型错误。源码实现为什么这么快uniq 的完整实现位于 src/array/uniq.ts全函数只有一行export function uniqT(arr: readonly T[]): T[] { return [...new Set(arr)]; }这行代码同时完成了三件事new Set(arr)利用Set的 SameValueZero 语义自动去重。Set内部通常基于哈希表实现插入与查找平均为 O(1)因此整体去重复杂度为 O(n)——这是与 lodash 经典双循环或排序实现相比的主要性能优势展开运算符[...]将Set迭代回普通数组且Set的迭代顺序就是元素首次插入的顺序天然保持了原始数组的次序返回新数组与输入数组是不同引用不会修改原数组。相等语义SameValueZerouniq 去重时遵循与Set、Map、Array.prototype.includes一致的SameValueZero相等语义这一点与使用的朴素去重实现存在明显差异NaN与NaN被视为相等会认为它们不相等0与-0被视为相等同样认为它们不相等字符串、数字按值比较对象按引用比较。测试 src/array/uniq.spec.ts 中的special values用例直接验证了这一行为const arr [NaN, NaN, 0, -0, Infinity, -Infinity]; expect(uniq(arr)).toEqual([NaN, 0, Infinity, -Infinity]);即两个NaN只保留一个、0与-0只保留一个。边界行为测试用例验证src/array/uniq.spec.ts 对 uniq 的各种边界情况做了系统验证这些行为是使用时的关键事实依据场景输入输出说明数字去重[11, 2, 3, 44, 11, 2, 3][11, 2, 3, 44]基础去重字符串去重[a, b, b, c, a][a, b, c]基础去重布尔去重[true, false, true, false, false][true, false]布尔值参与比较空值去重[null, undefined, null, undefined][null, undefined]null与undefined彼此不同空数组[][]返回空数组混合类型[1, a, 2, b, 1, a][1, a, 2, b]不同类型互不相等顺序保持[1, 2, 2, 3, 4, 4, 5][1, 2, 3, 4, 5]保留首次出现顺序稀疏数组[1, , 2, undefined, 3, , 2]含空洞[1, undefined, 2, 3]空洞被折叠undefined正常保留特殊值[NaN, NaN, 0, -0, Infinity, -Infinity][NaN, 0, Infinity, -Infinity]SameValueZero 语义此外测试还明确断言了两个重要的不可变承诺// 返回新数组而非原数组引用 expect(result).not.toBe(array); // 不修改原数组 const array [1, 2, 3, 2, 1, 3]; uniq(array); expect(array).toEqual([1, 2, 3, 2, 1, 3]);对稀疏数组sparse array的处理值得留意展开运算符对空洞迭代时得到undefined因此[1, , 2]这类含空洞的数组在去重时空洞会被折叠为undefined与已有的undefined去重合并。若你的数据依赖稀疏数组的语义请留意这一差异。相关函数uniqBy 与 uniqWith 的选型uniq 处理的是元素自身相等的去重。当判定标准不再是元素本身时es-toolkit 还提供了两个变体三者均从 src/array/index.ts 统一导出uniqBy按变换结果去重当需要把每个元素映射为一个比较键再按该键去重时使用 uniqBy只保留映射结果相同元素中的第一个import { uniqBy } from es-toolkit/array; // 按年龄去重 const users [ { id: 1, name: john, age: 30 }, { id: 2, name: jane, age: 30 }, { id: 3, name: joe, age: 25 }, { id: 4, name: jenny, age: 25 }, ]; const uniqueByAge uniqBy(users, user user.age); // [{ id: 1, name: john, age: 30 }, { id: 3, name: joe, age: 25 }]其参数签名为uniqBy(arr, mapper)其中mapper为(item: T, index: number, array: readonly T[]) U即除元素本身外还可使用索引与整个数组。uniqWith按自定义比较函数去重当两个元素是否相等需要自定义判断逻辑例如差值小于阈值、忽略大小写、多字段联合比较时使用 uniqWithimport { uniqWith } from es-toolkit/array; // 数字差小于 1 视为相同 const numbers [1.2, 1.5, 2.1, 3.2, 5.7, 5.3, 7.19]; const result uniqWith(numbers, (a, b) Math.abs(a - b) 1); console.log(result); // [1.2, 3.2, 5.7, 7.19] // 忽略大小写比较字符串 const words [Apple, APPLE, banana, Banana, cherry]; const uniqueCaseInsensitive uniqWith(words, (a, b) a.toLowerCase() b.toLowerCase()); console.log(uniqueCaseInsensitive); // [Apple, banana, cherry]其比较函数签名为(item1: T, item2: T) boolean两个元素相等时返回true。注意uniqWith需要逐一与已保留元素比较最坏情况下复杂度为 O(n²)元素规模较大时应优先评估能否用uniqBy转化为 O(n) 的键去重。选型建议元素按值/引用去重且无需自定义逻辑 →uniq需要按某个字段或映射结果去重 →uniqBy保持 O(n)需要完全自定义的相等判断如模糊匹配、忽略大小写→uniqWith注意 O(n²) 最坏情况。扩展用法compat 与 fp 变体compat 版本兼容 lodash 入参习惯src/compat/array/uniq.ts 提供了兼容层接受ArrayLikeT | null | undefined作为输入内部先经isArrayLike校验、Array.from归一化后再调用核心实现对null、undefined或非类数组对象返回[]与 lodash 的行为对齐方便迁移既有代码uniq(null); // [] uniq(undefined); // [] uniq(hello); // 字符串按类数组处理fp 版本惰性求值src/fp/array/uniq.ts 提供了函数式变体与pipe配合使用import { pipe, uniq } from es-toolkit/fp; pipe([1, 2, 2, 3, 3, 3], uniq()); // [1, 2, 3]从源码结构看fp 版通过combineEagerAndLazyFunctions组合了贪心与惰性两条路径在pipe管道中uniq 会以惰性Sink形式工作内部维护一个Set仅当遇到新值时向下游发射从而支持流式处理避免中间结果整体物化带来的额外开销。总结uniq 是 es-toolkit 中一个小而精的数组工具一行[...new Set(arr)]即获得 O(n) 去重、SameValueZero 相等语义与首次出现顺序保持三大保证且测试覆盖了空数组、混合类型、稀疏数组、NaN 与 ±0 等全部关键边界。配合 uniqBy按键去重与 uniqWith自定义比较两个变体可以覆盖绝大多数去重诉求若需迁移 lodash 代码或使用函数式管道仓库还提供了 compat 与 fp 两种形态。相关实现与测试可分别参阅 src/array/uniq.ts、src/array/uniq.spec.ts以及 src/compat/array/uniq.ts、src/fp/array/uniq.ts。【免费下载链接】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),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →