Lodash源码阅读-uniqBy

Lodash 源码阅读-uniqBy

概述

uniqBy 函数用于创建一个数组的去重版本,通过指定的迭代器函数为数组的每个元素生成用于比较的标准。它只保留数组中每个元素(基于迭代器返回值)的第一次出现。结果数组中元素的顺序取决于它们在原数组中首次出现的位置。

前置学习

依赖函数

  • baseUniq:内部函数,实现数组去重的核心逻辑
  • getIteratee:获取适当的迭代器函数

技术知识

  • 数组去重算法
  • 高阶函数:接收函数作为参数
  • 迭代器函数:为每个数组元素生成比较标准
  • JavaScript 中的 SameValueZero 比较
  • 函数柯里化和函数式编程

源码实现

javascript 复制代码
function uniqBy(array, iteratee) {
  return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];
}

实现思路

uniqBy 函数的实现非常简洁,但功能强大。它首先检查输入数组是否存在且有长度,如果是,则调用内部的 baseUniq 函数进行实际的去重操作;否则返回空数组。

在调用 baseUniq 时,它传入了两个参数:

  1. 原始数组 array
  2. 通过 getIteratee(iteratee, 2) 获取的迭代器函数,其中 2 表示迭代器函数应接收一个参数(数组元素)

uniqBy 的核心在于允许用户自定义如何生成比较元素唯一性的标准,这使得去重过程更加灵活和强大。

源码解析

让我们逐行分析 uniqBy 函数的实现:

javascript 复制代码
function uniqBy(array, iteratee) {

函数定义,接收两个参数:

  • array:要去重的数组
  • iteratee:迭代器函数,为每个元素生成比较的键值
javascript 复制代码
return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];

这行代码完成了主要工作:

  1. array && array.length 检查数组是否存在且有长度
  2. 如果条件为真,调用 baseUniq 函数
    • 第一个参数是原始数组 array
    • 第二个参数是通过 getIteratee(iteratee, 2) 获取的迭代器函数
  3. 如果条件为假(数组不存在或为空),返回空数组 []

getIteratee(iteratee, 2) 是个关键部分,它根据传入的 iteratee 类型返回一个适当的函数:

  • 如果 iteratee 是函数,它会被直接使用
  • 如果 iteratee 是字符串,它会被转换为属性访问函数(如 _.property 的效果)
  • 如果 iteratee 是对象,它会被转换为匹配器函数(如 _.matches 的效果)
  • 如果 iteratee 是数组,它会被转换为属性-值匹配函数(如 _.matchesProperty 的效果)
  • 如果 iteratee 为 null 或 undefined,会返回 identity 函数(返回原值)

参数 2 表示返回的迭代器函数的参数数量 (arity),这里指定为接收一个参数。

baseUniq 执行时,它会使用这个迭代器函数为每个数组元素计算一个 "键",然后使用这些键来确定元素是否唯一。相同键的元素中,只有第一个会被保留在结果数组中。

总结

uniqBy 函数是 Lodash 的一个实用工具,通过允许自定义比较标准,它提供了比简单的 uniq 函数更强大的数组去重能力。

这个函数的主要特点包括:

  1. 灵活性:支持多种形式的迭代器(函数、字符串路径、对象匹配器等)
  2. 保留原始值:虽然比较是基于迭代器返回的值,但结果数组中保留的是原始元素
  3. 保持顺序:保留了元素在原数组中的相对顺序(首次出现的位置)
  4. 参数验证:优雅地处理边缘情况,如空数组或 null 值

在实际应用中,uniqBy 特别适用于:

  • 去除具有相同特定属性值的对象
  • 基于复杂计算进行去重
  • 实现类似 SQL 中的 "GROUP BY" 功能(取每组第一个元素)

从设计模式角度看,uniqBy 使用了策略模式(允许用户选择不同的去重策略)和委托模式(将具体实现委托给内部函数)。这种设计让 API 保持简洁易用,同时内部实现可以更加复杂和高效。

相关推荐
刺客-Andy9 分钟前
React 第七十节 Router中matchRoutes的使用详解及注意事项
前端·javascript·react.js
前端工作日常25 分钟前
我对eslint的进一步学习
前端·eslint
禁止摆烂_才浅1 小时前
VsCode 概览尺、装订线、代码块高亮设置
前端·visual studio code
程序员猫哥1 小时前
vue跳转页面的几种方法(推荐)
前端
代码老y2 小时前
十年回望:Vue 与 React 的设计哲学、演进轨迹与生态博弈
前端·vue.js·react.js
一条上岸小咸鱼2 小时前
Kotlin 基本数据类型(五):Array
android·前端·kotlin
zzywxc7872 小时前
详细探讨AI在金融、医疗、教育和制造业四大领域的具体落地案例,并通过代码、流程图、Prompt示例和图表等方式展示这些应用的实际效果。
开发语言·javascript·人工智能·深度学习·金融·prompt·流程图
大明882 小时前
用 mouseover/mouseout 事件代理模拟 mouseenter/mouseleave
前端·javascript
小杨梅君2 小时前
vue3+vite中使用自定义element-plus主题配置
前端·element
一个专注api接口开发的小白2 小时前
Python + 淘宝 API 开发:自动化采集商品数据的完整流程
前端·数据挖掘·api