• 相信很多前端同学甚至非前端都或多或少使用过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. ALTER TABLE 加字段的时候到底锁不锁表?

    Mysql5.6版本之前 更新步骤 对原始表加写锁 按照原始表和执行语句的定义,重新定义一个空的临时表. 对临时表进行添加索引(如果有). 再将原始表中的数据逐条Copy到临时表中. 当原始表中的所有 ...

  2. 启动HDFS伪分布式环境时报权限错误

    问题描述 操作系统:Ubuntu18.04 LTS HDFS版本:hadoop-3.2.3 普通用户登录,参照官方文档在单机上安装伪分布式环境时,启动HDFS报权限错误. 具体报错信息如下: $ ./ ...

  3. 【架构师视角系列】QConfig配置中心系列之Client端(二)

    目录 声明 配置中心系列文章 一.架构 一.客户端架 1.Server 职责 (1)配置管理 (2)配置发布 (3)配置读取 2.Client 职责 (1)配置拉取 (2)配置注入 (3)配置变更监听 ...

  4. 【LeetCode栈与队列#04】逆波兰表达式求值(仍然是经典的栈操作)

    逆波兰表达式求值 力扣题目链接(opens new window) 根据 逆波兰表示法,求表达式的值. 有效的运算符包括 + , - , * , / .每个运算对象可以是整数,也可以是另一个逆波兰表达 ...

  5. RabbitMQ零碎整理,总有一天在你工作中会用到

    概念 MQ:消息队列(消息中间件),开源的基于AMQP协议的消息中间件,异步 解耦 削峰 核心作用:1.异步 2.解耦 3.消息通信 RabbitMQ 消息的类型:1.点对点 2.Worker 3.发 ...

  6. 第123篇: JS函数属性与方法

    好家伙,本篇为<JS高级程序设计>第十章"函数"学习笔记 ECMAScript 中的函数是对象,因此有属性和方法. 1.函数属性 每个函数都有两个属性:length 和 ...

  7. web模块化

    CommonJS-----是一种后端js规范,是nodejs遵循的一种编写js模块的规范引入模块-------require('模块路径')定义模块 ------ exports.模块名= funct ...

  8. 【Azure 事件中心】Event Hub Client 连接超时(OperationTimeout)测试及解说

    Azure Event Hub(Azure事件中心) 是大数据流式处理平台和事件引入服务. 它可以每秒接收和处理数百万个事件.在我们的使用中,需要代码编写的是两个部分:事件生产者和事件接收者 事件生成 ...

  9. 基于 Nebula Graph 构建图学习能力

    本文首发于 Nebula Graph Community 公众号 经常看技术文章的小伙伴可能会留意到除了正在阅读的那篇文章,在文章页面的正文下方或者右侧区域会有若干同主题.同作者的文章等你阅读:经常逛 ...

  10. 文心一言 VS 讯飞星火 VS chatgpt (206)-- 算法导论15.4 3题

    三.设计 LCS-LENGTH的带备忘的版本,运行时间为O(mn).需要写代码的时候,请用go语言. 文心一言,代码正常运行: LCS-LENGTH(Longest Common Subsequenc ...