• 相信很多前端同学甚至非前端都或多或少使用过lodash库,我们都知道lodash是一个非常丰富的前端工具库,比如最常用的防抖和节流,使用lodash都能很快实现,在github上更是有着58.7k的star数。但最近出现的Radash库,号称lodash plus版本,比之更新、更小、更全面、源码更易于理解。
  • 阅读本文你能了解些什么?
    1. radash是什么;
    2. 它相较于lodash有哪些优势;
    3. radash 数组相关方法介绍源码解析

认识Radash

一句话介绍:radash是一个强大的零依赖的前端工具库。如果你会使用lodash,那么你使用radash将没有任何门槛。

使用Radash有哪些优势?

  • 零依赖,radash不依赖任何第三方库,仅在自己的源码里面去实现功能,所以非常的轻量。使用它你只需要加载radash本身;
  • Typescript编写,使用起来更安全,不用担心变量类型问题;
  • 全面支持es6+的新特性。它去除了lodash身上一些过时的方法(这些方法能够使用es6+新特性快速简单实现);
  • 方法更全面。包含数组相关、对象相关、排序相关、字符串相关、优化相关等等等等...,几乎能满足你能想到的前端工具方法。
  • 源码更易于理解。我们甚至可以说radash的某些方法的实现时直接而暴力的(这点你会在我后续的方法源码介绍中有所感受)。

Radash相关方法如何使用

  1. 下载radash

    npm install radash --save
    // 或 yarn下载
    yarn add radash
  2. 引入你需要的方法

    import { alphabetical } from 'radash'
  3. 按照要求传入相关参数就可以使用了。

Radash的数组相关操作方法详解

注意:以下我们示例将直接使用,不再进行引入操作,实际使用时记得先引入再使用

alphabetical:把对象数组按照选定key的value的字母顺序排列

  1. 用法说明

    • 参数:目标对象数组、用于排序的属性的回调函数、第三个参数可选(不传是升序排序,传入desc字符则表示降序排序);
    • 返回值:排序后的数组。
  2. 基础使用代码示例

    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]  
  3. 源码解析

    // 定义一个泛型函数 `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:返回对象数组中满足条件的对象

  1. 用法说明

    • 参数:目标对象数组、条件函数;
    • 返回值:满足条件的对象。
  2. 基础代码示例

    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 }
  3. 源码解析

    // 定义一个泛型函数 `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:把一个数组尽量均匀的分成多个数组

  1. 用法说明

    • 参数:目标数组、分组个数n;
    • 返回值:分组后的二维数组。
  2. 基础代码示例
    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']
    // ]
  3. 源码解析
    // 定义一个泛型函数 `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)
    })
    }
    • 方法工作流程说明

      1. 首先,使用 Math.ceil 函数计算出给定数组大小和子数组大小的情况下,需要多少个子数组群组。因为 Math.ceil 向上取整,这确保了即使最后一个群组不满也会被创建;
      2. 接着,创建一个新的数组,这个数组的长度是我们刚才计算出的群组数量 clusterCount。使用 fill(null) 方法将其填充为 null,这样我们就可以在其上使用 map 方法;
      3. 然后,对这个新数组使用 map 方法,对于其中的每个元素(最初都是 null),我们计算原数组 list 中对应的子数组应该从哪里开始(i * size),到哪里结束(i * size + size),并使用 slice 方法提取这个子数组;
      4. 最终,我们得到一个新的数组,它由原数组 list 切分成多个子数组组成,每个子数组的最大长度由 size 参数决定。

counting:统计对象数组中每个唯一标识符的出现次数

  1. 用法说明

    • 参数:目标对象数组、条件函数(内部是传入目标对象,返回对象身上的某一项——根据这项来做统计);
    • 返回值:统计对象。
  2. 基础代码示例
    const skt = [
    {
       name: 'Ra',
       culture: 'egypt'
    },
    {
       name: 'Zeus',
       culture: 'greek'
    },
    {
       name: 'Loki',
       culture: 'greek'
    }
    ] counting(gods, g => g.culture) // => { egypt: 1, greek: 2 }
  3. 源码解析
    // 定义一个泛型函数 `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>) // 初始化累加器为一个空对象。
    }
    • 方法工作流程说明

      1. 接收一个数组 list 和一个 identity 函数,后者用于指定如何从数组项中提取唯一标识符;
      2. 如果传入的 list 为空,返回一个空的记录对象;
      3. 使用 reduce 方法遍历数组。reduce 的累加器 acc 是一个对象,其键是通过 identity 函数从数组项中提取的唯一标识符,值是标识符出现的次数;
      4. 在每次迭代中,从当前项 item 中提取唯一标识符 id。如果 acc 中已经存在 id 键,就将其值加1;如果不存在,就将其值设置为1;
      5. 最终,返回这个累加器对象,它是一个记录对象,其键是唯一标识符,值是对应的出现次数。

diff:返回数组1中出现但是没在数组2中出现的项

  1. 用法说明

    • 参数:目标数组1、目标数组2;
    • 返回值:包含符合项的数组。
  2. 基础代码示例
    import { diff } from 'radash'
    
    const oldWorldGods = ['rng', 'uzi']
    const newWorldGods = ['vishnu', 'uzi'] diff(oldWorldGods, newWorldGods) // => ['rng']
  3. 源码解析
    // 定义一个泛型函数 `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)])
    }
    • 方法工作流程说明:

      1. 检查 rootother 数组是否都为空或未定义,如果是,则返回空数组;
      2. 如果 root 数组为空或未定义,而 other 数组不是,返回 other 数组的副本;
      3. 如果 other 数组为空或未定义,而 root 数组不是,返回 root 数组的副本;
      4. 如果两个数组都不为空,使用 other 数组的元素创建一个记录对象 bKeysidentity 函数用于为每个元素提取唯一标识符,这些标识符作为 bKeys 对象的键,其对应的值被设置为 true
      5. 使用 filter 方法遍历 root 数组,返回那些其通过 identity 函数提取的唯一标识符不在 bKeys 对象中的元素。这些元素构成了 rootother 数组的差异集。

下期我们将介绍以下方法

提示:如果是简单使用的话可以直接按照介绍选择合适的方法进行使用,我们后续会详细介绍。

  1. first:获取数组第一项,不存在返回默认值;
  2. flat:数组扁平化 —— 把多维数组转为一维数组;
  3. fork:按条件将数组拆分成两个数组,满足条件的一个,不满足条件的一个;
  4. group:根据条件函数指定的唯一标识符出现次数对数组进行排序;
  5. intersects:判断两个数组是否有公共项,返回一个布尔值。

写在后面

后续作者会整理一份方法目录上传,方便没法访问外网的同学对照查看使用。

大家有任何问题或者见解,欢迎评论区留言交流!!!

点击访问:Radash官网

参考文章:Lodash is dead. Long live Radash.

lodash已死?radash最全使用介绍(附源码详细说明)—— Array方法篇(1)的更多相关文章

  1. 阅读lodash源码之旅数组方法篇-compact和concat

    鲁迅说过:只有阅读过优秀库源码的人,才能配的上是真正的勇士. compact 创建一个新数组,包含原数组中所有的非假值元素.例如false, null,0, "", undefin ...

  2. php 品牌全车零件订购平台( 带采集数据 及 账号自动登陆【已绕过https证书加密】,php源码 ,QQ: 876635409 )

    php捷豹路虎 品牌全车零件订购平台  ( 带采集数据 及 账号自动登陆[已绕过https证书加密],php源码 ,QQ: 876635409 [由于咨询用户太多,请备注:汽车配件]) 一.php+m ...

  3. IPerf——网络测试工具介绍与源码解析(4)

    上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直 ...

  4. 日志组件Log2Net的介绍和使用(附源码开源地址)

    Log2Net是一个用于收集日志到数据库或文件的组件,支持.NET和.NetCore平台. 此组件自动收集系统的运行日志(服务器运行情况.在线人数等).异常日志.程序员还可以添加自定义日志. 该组件支 ...

  5. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  6. 死磕 java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

  7. 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁

    问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...

  8. Vue源码详细解析:transclude,compile,link,依赖,批处理...一网打尽,全解析!

    用了Vue很久了,最近决定系统性的看看Vue的源码,相信看源码的同学不在少数,但是看的时候却发现挺有难度,Vue虽然足够精简,但是怎么说现在也有10k行的代码量了,深入进去逐行查看的时候感觉内容庞杂并 ...

  9. Android IntentService使用介绍以及源码解析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...

  10. SpringMVC+Maven开发项目源码详细介绍

    代码地址如下:http://www.demodashi.com/demo/11638.html Spring MVC概述 Spring MVC框架是一个开源的Java平台,为开发强大的基于Java的W ...

随机推荐

  1. SQLite 入门教程

    不是 MySQL 用不起,而是 SQLite 更有性价比,绝大多数的 Web 应用 SQLite 都可以满足. SQLite 是一个用 C 语言编写的开源.轻量级.快速.独立且高可靠性的 SQL 数据 ...

  2. 关于Image的mode属性(多图示例)

    一.总览 Image的mode是指图片裁剪.缩放的模式,共有14个合法值,分别是: /** 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 */ scaleToFill ...

  3. Ubuntu虚拟机开机显示initramfs

    因为我的虚拟机路径放在了移动硬盘当中,所以连接有点失常就断开了,紧接着虚拟机也异常关闭了. 重启后进入了initramfs界面 查看出错的分区,如下图所示,是/dev/sda5分区有损坏 解决方法: ...

  4. 【Azure 应用服务】App Service 项目部署成功后,应用连接 Azure Redis时报错 Could not get a resource from the pool

    问题描述 App Service 项目部署成功后,需要连接到同在云上的Redis服务, Redis启动了专用终结点,只能在于Redis同一个VNET(虚拟网络)的资源能够访问.在进入App Servi ...

  5. 2024年,提升Windows开发和使用体验实践 - 小工具篇

    前言 本来是一篇文章的,不知不觉写成了系列. 其实开工那几天就已经写好了长文,这几天一忙就没连着发了. 本文介绍一些 Windows 上用的小工具. 美化/折腾/小工具 虽然这是在用 Windows ...

  6. 【规范】Apifox就应该这么玩

    前言 缘由 好的工具就要配好的玩法 起因是最近在回顾项目时,看到了年事已高并且长时间不用的Postman,发现之前自己整理的接口文档十分混乱且没有规律.遂打开现在使用的Apifox,将本狗目前项目中使 ...

  7. RocketMQ(11) 消息重试机制和死信队列

    七.消息发送重试机制 1 说明 Producer对发送失败的消息进行重新发送的机制,称为消息发送重试机制,也称为消息重投机制. 对于消息重投,需要注意以下几点: 生产者在发送消息时,若采用同步或异步发 ...

  8. 我和我的DBA之路

    这几天,突然想写写这些年的工作总结,毕业至今快20年的回顾. 想到20年前,在做毕业设计的时候,当时是学的机械工程类专业,因为带毕业设计的老师兼职企业有个门户网站的需求,而我又会做点网站设计,带的老师 ...

  9. CPNtools协议建模安全分析--ML语言之颜色集定义(六)

    之前一直在怀疑我是不是因为对CPN Tools的原理结构还是不够理解,对Petri网的还没有弄清楚,越往后面看这种质疑越来越严重. 之前说CPN Tools在对称和非对称算法中不能形式化的问题,后续看 ...

  10. Harbor 2.1.2 安装部署

    环境 首先需要准备好 Docker + Docker-Compose 环境,Docker 在 CentOS 7.x 的安装教程请参考 这篇文章,后续文章假设你已经安装好了上述环境. 安装 标准安装 首 ...