• 相信很多前端同学甚至非前端都或多或少使用过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. 记一次 QT VS Tools 无法配置 version 的问题

    问题概述: 在 QT VS Tools 拓展工具中添加多个 qt 版本的静态库时,发现输入完 Name 和 Path 之后点击 OK,新输入的 version 路径并没有保存成功 测试的 QT VS ...

  2. win32 - WaitForMultipleObjects的使用

    创建5个线程,并无限期地打印某些内容 #include <Windows.h> #include <stdio.h> DWORD IDs[5]; DWORD WINAPI Th ...

  3. Nacos2作为Dubbo3的配置中心踩坑总结

    本文阐述问题所使用的组件版本分别是,Dubbo:3.2.4,Nacos服务端:2.2.3,Nacos客户端:2.2.4. 在Dubbo3的官方文档中,关于如何使用Nacos作为配置中心的详细介绍参考: ...

  4. 常见Python问题及解决办法

    文件编码问题 如果Python文件中存在中文注释,在运行时报错"SyntaxError: Non-ASCII character '\xe7' in file". 解决办法: 在文 ...

  5. 2021-07-30 JavaScript中常用数据的判断

    为什么要判断一个变量的常用数据? 实际业务场景里,一个变量的数据是否合法或符合预期,会影响到项目中用到的UI组件库特定组件的运行.比如element-ui中的el-select组件,单选时绑定的数据不 ...

  6. 【Azure Logic App】使用Outlook.com发送邮件遇到429报错

    问题描述 在Logic App中使用 Outlook.com组件发送邮件,遇见了outlook connection报429的错误 {"error":{"code&quo ...

  7. 【Azure 存储服务】Azure Blob下面是否可以创建子文件夹

    问题描述 如何在Azure Storage Account(存储账户) 门户上为 Container 创建子文件夹? 问题解决 经验证,没有办法在门户上直接创建文件夹,不过可以使用Azure Stor ...

  8. UI自动化测试中图片验证码的处理?

    首先定位验证码image的元素,然后通过element.screenshot('a.png')截图保存到本地,通过第三方接口识别本地图片验证码

  9. RocketMQ(6) offset管理

    这里的offset指的是Consumer的消费进度offset. 消费进度offset是用来记录每个Queue的不同消费组的消费进度的.根据消费进度记录器的不同,可以分为两种模式:本地模式和远程模式. ...

  10. linux下查看文件时显示行号

    1.用 vi 或 vim 打开文件后显示行号: 显示当前行号: :nu 显示所有行号: :set nu     2.设置服务器显示行号 2.1:编辑~/.vimrc文件,在该文件中加入         ...