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 ...
随机推荐
- ALTER TABLE 加字段的时候到底锁不锁表?
Mysql5.6版本之前 更新步骤 对原始表加写锁 按照原始表和执行语句的定义,重新定义一个空的临时表. 对临时表进行添加索引(如果有). 再将原始表中的数据逐条Copy到临时表中. 当原始表中的所有 ...
- 启动HDFS伪分布式环境时报权限错误
问题描述 操作系统:Ubuntu18.04 LTS HDFS版本:hadoop-3.2.3 普通用户登录,参照官方文档在单机上安装伪分布式环境时,启动HDFS报权限错误. 具体报错信息如下: $ ./ ...
- 【架构师视角系列】QConfig配置中心系列之Client端(二)
目录 声明 配置中心系列文章 一.架构 一.客户端架 1.Server 职责 (1)配置管理 (2)配置发布 (3)配置读取 2.Client 职责 (1)配置拉取 (2)配置注入 (3)配置变更监听 ...
- 【LeetCode栈与队列#04】逆波兰表达式求值(仍然是经典的栈操作)
逆波兰表达式求值 力扣题目链接(opens new window) 根据 逆波兰表示法,求表达式的值. 有效的运算符包括 + , - , * , / .每个运算对象可以是整数,也可以是另一个逆波兰表达 ...
- RabbitMQ零碎整理,总有一天在你工作中会用到
概念 MQ:消息队列(消息中间件),开源的基于AMQP协议的消息中间件,异步 解耦 削峰 核心作用:1.异步 2.解耦 3.消息通信 RabbitMQ 消息的类型:1.点对点 2.Worker 3.发 ...
- 第123篇: JS函数属性与方法
好家伙,本篇为<JS高级程序设计>第十章"函数"学习笔记 ECMAScript 中的函数是对象,因此有属性和方法. 1.函数属性 每个函数都有两个属性:length 和 ...
- web模块化
CommonJS-----是一种后端js规范,是nodejs遵循的一种编写js模块的规范引入模块-------require('模块路径')定义模块 ------ exports.模块名= funct ...
- 【Azure 事件中心】Event Hub Client 连接超时(OperationTimeout)测试及解说
Azure Event Hub(Azure事件中心) 是大数据流式处理平台和事件引入服务. 它可以每秒接收和处理数百万个事件.在我们的使用中,需要代码编写的是两个部分:事件生产者和事件接收者 事件生成 ...
- 基于 Nebula Graph 构建图学习能力
本文首发于 Nebula Graph Community 公众号 经常看技术文章的小伙伴可能会留意到除了正在阅读的那篇文章,在文章页面的正文下方或者右侧区域会有若干同主题.同作者的文章等你阅读:经常逛 ...
- 文心一言 VS 讯飞星火 VS chatgpt (206)-- 算法导论15.4 3题
三.设计 LCS-LENGTH的带备忘的版本,运行时间为O(mn).需要写代码的时候,请用go语言. 文心一言,代码正常运行: LCS-LENGTH(Longest Common Subsequenc ...