lodash已死?radash最全使用介绍(附源码详细说明)—— Array方法篇(1)
- 相信很多前端同学甚至非前端都或多或少使用过lodash库,我们都知道lodash是一个非常丰富的前端工具库,比如最常用的防抖和节流,使用lodash都能很快实现,在github上更是有着58.7k的star数。但最近出现的Radash库,号称lodash plus版本,比之更新、更小、更全面、源码更易于理解。
- 阅读本文你能了解些什么?
- radash是什么;
- 它相较于lodash有哪些优势;
- radash 数组相关方法介绍及源码解析。
认识Radash
一句话介绍:radash是一个强大的零依赖的前端工具库。如果你会使用lodash,那么你使用radash将没有任何门槛。
使用Radash有哪些优势?
- 零依赖,radash不依赖任何第三方库,仅在自己的源码里面去实现功能,所以非常的轻量。使用它你只需要加载radash本身;
- Typescript编写,使用起来更安全,不用担心变量类型问题;
- 全面支持es6+的新特性。它去除了lodash身上一些过时的方法(这些方法能够使用es6+新特性快速简单实现);
- 方法更全面。包含数组相关、对象相关、排序相关、字符串相关、优化相关等等等等...,几乎能满足你能想到的前端工具方法。
- 源码更易于理解。我们甚至可以说radash的某些方法的实现时直接而暴力的(这点你会在我后续的方法源码介绍中有所感受)。
Radash相关方法如何使用
下载radash
npm install radash --save
// 或 yarn下载
yarn add radash
引入你需要的方法
import { alphabetical } from 'radash'
按照要求传入相关参数就可以使用了。
Radash的数组相关操作方法详解
注意:以下我们示例将直接使用,不再进行引入操作,实际使用时记得先引入再使用
alphabetical:把对象数组按照选定key的value的字母顺序排列
用法说明
- 参数:目标对象数组、用于排序的属性的回调函数、第三个参数可选(不传是升序排序,传入
desc字符则表示降序排序); - 返回值:排序后的数组。
- 参数:目标对象数组、用于排序的属性的回调函数、第三个参数可选(不传是升序排序,传入
基础使用代码示例
const ig = [
{
name: 'ning',
power: 100
},
{
name: 'rookie',
power: 98
},
{
name: 'jkl',
power: 95
},
{
name: 'theshy',
power: 100
}
]
// 这里输出的依然是对象数组,这里简单表示
alphabetical(ig, g => g.name) // => [jkl, ning, rookie, theshy]
alphabetical(ig, g => g.name, 'desc') // => [theshy, rookie, ning, jkl]
源码解析
// 定义一个泛型函数 `alphabetical`,接受一个泛型数组 `array`,
// 一个用于从数组项中获取排序依据字符串的函数 `getter`,
// 和一个可选的方向参数 `dir`,默认值为 'asc'(升序)。
export const alphabetical = <T>(
array: readonly T[],
getter: (item: T) => string,
dir: 'asc' | 'desc' = 'asc'
) => {
// 如果输入数组不存在或为空,直接返回一个空数组
if (!array) return []
// 定义一个升序比较函数,使用 `localeCompare` 方法比较通过 `getter` 获取的字符串。
const asc = (a: T, b: T) => `${getter(a)}`.localeCompare(getter(b))
// 定义一个降序比较函数,它将通过 `getter` 获取的字符串逆序比较。
const dsc = (a: T, b: T) => `${getter(b)}`.localeCompare(getter(a))
// 使用 `slice` 方法克隆数组,避免修改原数组,然后根据 `dir` 参数选择排序函数进行排序。
return array.slice().sort(dir === 'desc' ? dsc : asc)
}
- 方法工作流程说明:
这个函数的作用是对任何类型的数组进行排序,排序依据是数组每项通过
getter函数得到的字符串。调用localeCompare是为了正确地比较可能包含特殊字符的字符串。这个函数还允许用户指定排序方向,升序或降序;localeCompare是一个字符串方法,用于比较两个字符串,并返回一个表示这两个字符串在排序中相对位置的数字。该方法基于本地语言环境的排序规则进行比较,这意味着它可以正确地比较具有特定语言字符和变音符号的字符串。
当localeCompare被调用时,它将返回三种可能的值:- 如果字符串在排序中应该出现在比较字符串之前,则返回一个负数;
- 如果两个字符串相等(在排序中的位置相同),则返回 0;
- 如果字符串在排序中应该出现在比较字符串之后,则返回一个正数;
例如,利用
localeCompare方法可以正确地对包含德语、法语或西班牙语等特殊字符的字符串进行排序,而不仅仅是基于ASCII码值的简单比较。
- 方法工作流程说明:
boil:返回对象数组中满足条件的对象
用法说明
- 参数:目标对象数组、条件函数;
- 返回值:满足条件的对象。
基础代码示例
const rng = [
{
name: 'Uzi',
power: 100
},
{
name: 'Xiaohu',
power: 98
},
{
name: 'Ming',
power: 72
}
]
boil(gods, (a, b) => (a.power > b.power ? a : b)) // => { name: 'Uzi', power: 100 }
boil(gods, (a, b) => (a.power < b.power ? a : b)) // => { name: 'Ming', power: 72 }
源码解析
// 定义一个泛型函数 `boil`,它接受一个具有只读属性的泛型数组 `array`,
// 以及一个用于比较数组中两个元素并返回其中一个的比较函数 `compareFunc`。
export const boil = <T>(
array: readonly T[],
compareFunc: (a: T, b: T) => T
) => {
// 如果传入的数组不存在或长度为0,则函数返回 null。
if (!array || (array.length ?? 0) === 0) return null
// 使用数组的 `reduce` 方法应用 `compareFunc`,将数组归约为单一的值。
return array.reduce(compareFunc)
}
方法工作流程说明:
在这个函数中,
reduce方法接收compareFunc作为参数。reduce方法会遍历数组的所有元素,并且在每一步中应用compareFunc,将数组中的元素逐渐归约到一个单一的结果。compareFunc函数负责决定如何从两个元素中选择一个。
cluster:把一个数组尽量均匀的分成多个数组
- 用法说明
- 参数:目标数组、分组个数n;
- 返回值:分组后的二维数组。
- 基础代码示例
const gods = ['Ra', 'Zeus', 'Loki', 'Vishnu', 'Icarus', 'Osiris', 'Thor', 'Apollo', 'Artemis', 'Athena'] cluster(gods, 3)
// => [
// [ 'Ra', 'Zeus', 'Loki' ],
// [ 'Vishnu', 'Icarus', 'Osiris' ],
// ['Thor', 'Apollo', 'Artemis'],
// ['Athena']
// ]
- 源码解析
// 定义一个泛型函数 `cluster`,它接收一个具有只读属性的泛型数组 `list`,
// 以及一个可选的数字参数 `size`,默认值为2,表示子数组的大小。
export const cluster = <T>(list: readonly T[], size: number = 2): T[][] => {
// 计算出需要多少个子数组群组来容纳原数组,确保即使不能完全平分也会创建一个额外的群组来容纳剩余的元素。
const clusterCount = Math.ceil(list.length / size)
// 创建一个新数组,长度为 `clusterCount`,初始填充为 `null`。
return new Array(clusterCount).fill(null).map((_c: null, i: number) => {
// 对于新数组中的每个元素,使用 `slice` 方法从原数组 `list` 中提取出相应的子数组。
// 子数组的开始索引是 `i * size`,结束索引是 `i * size + size`。
return list.slice(i * size, i * size + size)
})
}
- 方法工作流程说明:
- 首先,使用
Math.ceil函数计算出给定数组大小和子数组大小的情况下,需要多少个子数组群组。因为Math.ceil向上取整,这确保了即使最后一个群组不满也会被创建; - 接着,创建一个新的数组,这个数组的长度是我们刚才计算出的群组数量
clusterCount。使用fill(null)方法将其填充为null,这样我们就可以在其上使用map方法; - 然后,对这个新数组使用
map方法,对于其中的每个元素(最初都是null),我们计算原数组list中对应的子数组应该从哪里开始(i * size),到哪里结束(i * size + size),并使用slice方法提取这个子数组; - 最终,我们得到一个新的数组,它由原数组
list切分成多个子数组组成,每个子数组的最大长度由size参数决定。
- 首先,使用
- 方法工作流程说明:
counting:统计对象数组中每个唯一标识符的出现次数
- 用法说明
- 参数:目标对象数组、条件函数(内部是传入目标对象,返回对象身上的某一项——根据这项来做统计);
- 返回值:统计对象。
- 基础代码示例
const skt = [
{
name: 'Ra',
culture: 'egypt'
},
{
name: 'Zeus',
culture: 'greek'
},
{
name: 'Loki',
culture: 'greek'
}
] counting(gods, g => g.culture) // => { egypt: 1, greek: 2 }
- 源码解析
// 定义一个泛型函数 `counting`,它接收一个具有只读属性的泛型数组 `list`,
// 和一个函数 `identity`,该函数用于从数组每个元素中提取一个唯一标识符(可以是字符串、数字或符号)。
export const counting = <T, TId extends string | number | symbol>(
list: readonly T[],
identity: (item: T) => TId
): Record<TId, number> => {
// 如果传入的数组不存在,则返回一个空对象。
if (!list) return {} as Record<TId, number>
// 使用数组的 `reduce` 方法来累计每个唯一标识符的出现次数。
return list.reduce((acc, item) => {
// 使用 `identity` 函数从当前元素 `item` 中获取唯一标识符。
const id = identity(item)
// 如果 `acc`(累加器)中已经有这个标识符的记录,则增加它的计数,否则初始化为1。
acc[id] = (acc[id] ?? 0) + 1
// 返回更新后的累加器对象。
return acc
}, {} as Record<TId, number>) // 初始化累加器为一个空对象。
}
- 方法工作流程说明:
- 接收一个数组
list和一个identity函数,后者用于指定如何从数组项中提取唯一标识符; - 如果传入的
list为空,返回一个空的记录对象; - 使用
reduce方法遍历数组。reduce的累加器acc是一个对象,其键是通过identity函数从数组项中提取的唯一标识符,值是标识符出现的次数; - 在每次迭代中,从当前项
item中提取唯一标识符id。如果acc中已经存在id键,就将其值加1;如果不存在,就将其值设置为1; - 最终,返回这个累加器对象,它是一个记录对象,其键是唯一标识符,值是对应的出现次数。
- 接收一个数组
- 方法工作流程说明:
diff:返回数组1中出现但是没在数组2中出现的项
- 用法说明
- 参数:目标数组1、目标数组2;
- 返回值:包含符合项的数组。
- 基础代码示例
import { diff } from 'radash' const oldWorldGods = ['rng', 'uzi']
const newWorldGods = ['vishnu', 'uzi'] diff(oldWorldGods, newWorldGods) // => ['rng']
- 源码解析
// 定义一个泛型函数 `diff`,它接收两个具有只读属性的泛型数组 `root` 和 `other`,
// 以及一个可选的函数 `identity`,用于从数组元素中提取一个唯一标识符(默认为将元素直接作为标识符)。
export const diff = <T>(
root: readonly T[],
other: readonly T[],
identity: (item: T) => string | number | symbol = (t: T) =>
t as unknown as string | number | symbol
): T[] => {
// 如果两个数组都为空或未定义,则返回一个空数组。
if (!root?.length && !other?.length) return []
// 如果 `root` 数组未定义或为空,则返回 `other` 数组的副本。
if (root?.length === undefined) return [...other]
// 如果 `other` 数组未定义或为空,则返回 `root` 数组的副本。
if (!other?.length) return [...root]
// 使用 `other` 数组的元素创建一个记录对象 `bKeys`,键是通过 `identity` 函数提取的唯一标识符,值为 `true`。
const bKeys = other.reduce((acc, item) => {
acc[identity(item)] = true
return acc
}, {} as Record<string | number | symbol, boolean>)
// 过滤 `root` 数组,只返回不在 `bKeys` 记录对象中的元素。
return root.filter(a => !bKeys[identity(a)])
}
方法工作流程说明:
- 检查
root和other数组是否都为空或未定义,如果是,则返回空数组; - 如果
root数组为空或未定义,而other数组不是,返回other数组的副本; - 如果
other数组为空或未定义,而root数组不是,返回root数组的副本; - 如果两个数组都不为空,使用
other数组的元素创建一个记录对象bKeys。identity函数用于为每个元素提取唯一标识符,这些标识符作为bKeys对象的键,其对应的值被设置为true; - 使用
filter方法遍历root数组,返回那些其通过identity函数提取的唯一标识符不在bKeys对象中的元素。这些元素构成了root和other数组的差异集。
- 检查
下期我们将介绍以下方法
提示:如果是简单使用的话可以直接按照介绍选择合适的方法进行使用,我们后续会详细介绍。
- first:获取数组第一项,不存在返回默认值;
- flat:数组扁平化 —— 把多维数组转为一维数组;
- fork:按条件将数组拆分成两个数组,满足条件的一个,不满足条件的一个;
- group:根据条件函数指定的唯一标识符出现次数对数组进行排序;
- intersects:判断两个数组是否有公共项,返回一个布尔值。
写在后面
后续作者会整理一份方法目录上传,方便没法访问外网的同学对照查看使用。
大家有任何问题或者见解,欢迎评论区留言交流!!!
点击访问:Radash官网
参考文章:Lodash is dead. Long live Radash.
lodash已死?radash最全使用介绍(附源码详细说明)—— Array方法篇(1)的更多相关文章
- 阅读lodash源码之旅数组方法篇-compact和concat
鲁迅说过:只有阅读过优秀库源码的人,才能配的上是真正的勇士. compact 创建一个新数组,包含原数组中所有的非假值元素.例如false, null,0, "", undefin ...
- php 品牌全车零件订购平台( 带采集数据 及 账号自动登陆【已绕过https证书加密】,php源码 ,QQ: 876635409 )
php捷豹路虎 品牌全车零件订购平台 ( 带采集数据 及 账号自动登陆[已绕过https证书加密],php源码 ,QQ: 876635409 [由于咨询用户太多,请备注:汽车配件]) 一.php+m ...
- IPerf——网络测试工具介绍与源码解析(4)
上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直 ...
- 日志组件Log2Net的介绍和使用(附源码开源地址)
Log2Net是一个用于收集日志到数据库或文件的组件,支持.NET和.NetCore平台. 此组件自动收集系统的运行日志(服务器运行情况.在线人数等).异常日志.程序员还可以添加自定义日志. 该组件支 ...
- 死磕 java同步系列之Phaser源码解析
问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...
- 死磕 java同步系列之StampedLock源码解析
问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...
- 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁
问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...
- Vue源码详细解析:transclude,compile,link,依赖,批处理...一网打尽,全解析!
用了Vue很久了,最近决定系统性的看看Vue的源码,相信看源码的同学不在少数,但是看的时候却发现挺有难度,Vue虽然足够精简,但是怎么说现在也有10k行的代码量了,深入进去逐行查看的时候感觉内容庞杂并 ...
- Android IntentService使用介绍以及源码解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...
- SpringMVC+Maven开发项目源码详细介绍
代码地址如下:http://www.demodashi.com/demo/11638.html Spring MVC概述 Spring MVC框架是一个开源的Java平台,为开发强大的基于Java的W ...
随机推荐
- 记一次 QT VS Tools 无法配置 version 的问题
问题概述: 在 QT VS Tools 拓展工具中添加多个 qt 版本的静态库时,发现输入完 Name 和 Path 之后点击 OK,新输入的 version 路径并没有保存成功 测试的 QT VS ...
- win32 - WaitForMultipleObjects的使用
创建5个线程,并无限期地打印某些内容 #include <Windows.h> #include <stdio.h> DWORD IDs[5]; DWORD WINAPI Th ...
- Nacos2作为Dubbo3的配置中心踩坑总结
本文阐述问题所使用的组件版本分别是,Dubbo:3.2.4,Nacos服务端:2.2.3,Nacos客户端:2.2.4. 在Dubbo3的官方文档中,关于如何使用Nacos作为配置中心的详细介绍参考: ...
- 常见Python问题及解决办法
文件编码问题 如果Python文件中存在中文注释,在运行时报错"SyntaxError: Non-ASCII character '\xe7' in file". 解决办法: 在文 ...
- 2021-07-30 JavaScript中常用数据的判断
为什么要判断一个变量的常用数据? 实际业务场景里,一个变量的数据是否合法或符合预期,会影响到项目中用到的UI组件库特定组件的运行.比如element-ui中的el-select组件,单选时绑定的数据不 ...
- 【Azure Logic App】使用Outlook.com发送邮件遇到429报错
问题描述 在Logic App中使用 Outlook.com组件发送邮件,遇见了outlook connection报429的错误 {"error":{"code&quo ...
- 【Azure 存储服务】Azure Blob下面是否可以创建子文件夹
问题描述 如何在Azure Storage Account(存储账户) 门户上为 Container 创建子文件夹? 问题解决 经验证,没有办法在门户上直接创建文件夹,不过可以使用Azure Stor ...
- UI自动化测试中图片验证码的处理?
首先定位验证码image的元素,然后通过element.screenshot('a.png')截图保存到本地,通过第三方接口识别本地图片验证码
- RocketMQ(6) offset管理
这里的offset指的是Consumer的消费进度offset. 消费进度offset是用来记录每个Queue的不同消费组的消费进度的.根据消费进度记录器的不同,可以分为两种模式:本地模式和远程模式. ...
- linux下查看文件时显示行号
1.用 vi 或 vim 打开文件后显示行号: 显示当前行号: :nu 显示所有行号: :set nu 2.设置服务器显示行号 2.1:编辑~/.vimrc文件,在该文件中加入 ...