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 ...
随机推荐
- 【Android 逆向】【攻防世界】ill-intentions
1. apk 安装到手机, 啥输入框都没有 2. apk拖入到jadx中看看 public class MainActivity extends Activity { @Override // and ...
- 项目实战:Qt+OSG三维点云引擎(支持原点,缩放,单独轴或者组合多轴拽拖旋转,支持导入点云文件)
需求 开发基于osg的三维点云引擎模块. 1.基于x,y,z坐标轴. 2.可设置原点,设置缩放比例. 3.可设置y轴和z轴单位. 4.三轴中,XY为2D图的水平.竖直方向:Z轴,对应高度图 ...
- 鸿蒙开发学习(二)之ArkUI
目录 UI开发 布局 布局选择 布局位置 组件 容器组件 row.column RelativeContainer 列表 Tabs 子组件 页面路由 GitHub地址,欢迎star HmDemo: 鸿 ...
- 解析Spring中的循环依赖问题:初探三级缓存
什么是循环依赖? 这个情况很简单,即A对象依赖B对象,同时B对象也依赖A对象,让我们来简单看一下. // A依赖了B class A{ public B b; } // B依赖了A class B{ ...
- 【Azure Developer】Go语言调用Azure SDK如何登录到中国区Azure环境
问题描述 在 "使用 Azure SDK for Go 进行 Azure 身份验证" 文章中的 Go 示例代码进行登录Azure时,默认指向的是Globa Azure.当只修改AA ...
- 手把手教你蜂鸟e203协处理器的扩展
NICE协处理器 赛题要求: 对蜂鸟E203 RISC-V内核进行运算算子(譬如加解密算法.浮点运算.矢量运算等)的扩展,可通过NICE协处理器接口进行添加,也可直接实现RISC-V指令子集(譬如 ...
- 17. Class字节码指令解析
## 1. 概述 官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html Java 字节码对于虚拟机,就好像汇编语言对于 ...
- Java //数组的反转
1 //数组的反转 2 //方式一 3 System.out.println("数组的反转"); 4 5 // for(int i = 0; i <arr.length/2; ...
- 3、zookeeper在java使用的API
引入maven包 <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient< ...
- 机器学习从入门到放弃:卷积神经网络CNN(二)
一.前言 通过上一篇文章,我们大概了解了卷积是什么,并且分析了为什么卷积能在图像识别上起到巨大的作用.接下来,废话不多话,我们自己尝试动手搭建一个简易的CNN网络. 二.准备工作 在开始的时候,我们首 ...