lodash已死?radash最全使用介绍(附源码详细说明)—— Array方法篇(1)

  • 相信很多前端同学甚至非前端都或多或少使用过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

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

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

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

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

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

  1. 用法说明

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

    scss 复制代码
    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. 源码解析

    typescript 复制代码
    // 定义一个泛型函数 `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. 基础代码示例

    css 复制代码
    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. 源码解析

    php 复制代码
    // 定义一个泛型函数 `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. 基础代码示例

    css 复制代码
    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. 源码解析

    typescript 复制代码
    // 定义一个泛型函数 `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. 基础代码示例

    ini 复制代码
    const skt = [
      {
        name: 'Ra',
        culture: 'egypt'
      },
      {
        name: 'Zeus',
        culture: 'greek'
      },
      {
        name: 'Loki',
        culture: 'greek'
      }
    ]
    
    counting(gods, g => g.culture) // => { egypt: 1, greek: 2 }
  3. 源码解析

    typescript 复制代码
    // 定义一个泛型函数 `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. 基础代码示例

    javascript 复制代码
    import { diff } from 'radash'
    
    const oldWorldGods = ['rng', 'uzi']
    const newWorldGods = ['vishnu', 'uzi']
    
    diff(oldWorldGods, newWorldGods) // => ['rng']
  3. 源码解析

    typescript 复制代码
    // 定义一个泛型函数 `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.

相关推荐
爱吃的强哥10 分钟前
vue3 使用 vite 管理多个项目,实现各子项目独立运行,独立打包
前端·javascript·vue.js
谈不譚网安18 分钟前
CSRF请求伪造
前端·网络安全·csrf
TT模板24 分钟前
苹果cmsV10主题 MXonePro二开优化修复开源版
前端·html5
拖孩25 分钟前
【Nova UI】十一、组件库中 Icon 组件的测试、使用与全局注册全攻略
前端·javascript·vue.js·ui·sass
去伪存真30 分钟前
不用动脑,手把手跟着我做,就能掌握Gitlab+Jenkins提交代码自动构部署
前端·jenkins
天天扭码1 小时前
深入解析 JavaScript 中的每一类函数:从语法到对比,全面掌握适用场景
前端·javascript·面试
小希爸爸1 小时前
4、中医基础入门和养生
前端·后端
kooboo china.1 小时前
Tailwind CSS 实战:基于 Kooboo 构建企业官网页面(一)
前端·css·编辑器
uhakadotcom2 小时前
Fluid:云原生数据加速与管理的简单入门与实战
前端
鬼面瓷2 小时前
CAPL编程_03
前端·数据库·笔记