lodash已死?radash最全使用介绍(附源码说明)—— Array方法篇(4)
写在前面
tips:点赞 + 收藏 = 学会!
- 我们已经介绍了
radash
的相关信息和部分Array相关方法,详情可前往主页查看。 - 本篇我们继续介绍radash中Array的相关方法的剩余方法。
- 本期文章发布后,作者也会同步整理出Array方法的使用目录,包括文章说明和脑图说明。
- 因为方法较多,后续将专门发布一篇Array篇总结文档,方便大家查阅使用。
replace :查找指定项,并用传入的去替换;
- 使用说明
- 参数:对象数组1、用于替换的对象、条件函数。
- 返回值:替换目标项后的原始素组副本(新数组)。
- 使用代码示例
import { replace } from 'radash' const fish = [ { name: 'Marlin', weight: 105 }, { name: 'Bass', weight: 8 }, { name: 'Trout', weight: 13 } ] const salmon = { name: 'Salmon', weight: 22 } // read: replace fish with salmon where the name is Bass replace(fish, salmon, f => f.name === 'Bass') // => [marlin, salmon, trout]
- 源码解析
// 定义一个泛型函数 `replace`。 export const replace = <T>( // 第一个参数是一个具有只读属性的泛型数组 `list`。 list: readonly T[], // 第二个参数是一个新元素 `newItem`,它将用来替换数组中的一个现有元素。 newItem: T, // 第三个参数是一个 `match` 函数,它接受一个数组元素和它的索引, // 返回一个布尔值来指示是否找到了要被替换的元素。 match: (item: T, idx: number) => boolean ): T[] => { // 如果传入的数组 `list` 不存在,则返回一个空数组。 if (!list) return [] // 如果新元素 `newItem` 是未定义的,则返回 `list` 数组的副本。 if (newItem === undefined) return [...list] // 遍历 `list` 数组,寻找一个匹配的元素。 for (let idx = 0; idx < list.length; idx++) { const item = list[idx] // 使用 `match` 函数检查当前元素 `item` 是否是要被替换的元素。 if (match(item, idx)) { // 如果找到匹配的元素,创建并返回一个新数组,该数组是通过以下方式构建的: // 1. `list` 的开始到匹配位置之前的部分(不包括匹配位置)。 // 2. 新元素 `newItem`。 // 3. 匹配位置之后的 `list` 的剩余部分。 return [ ...list.slice(0, idx), newItem, ...list.slice(idx + 1, list.length) ] } } // 如果没有找到匹配的元素,返回 `list` 数组的副本。 return [...list] }
- 方法流程说明:
- 检查
list
是否存在。如果不存在,返回一个空数组。 - 检查
newItem
是否是未定义的。如果是,返回list
的副本。 - 遍历
list
数组。对于每个元素,使用match
函数检查该元素是否是要被替换的元素。 - 如果
match
函数返回true
,则在该位置用newItem
替换现有元素,并返回新构建的数组。 - 如果遍历结束后没有任何元素匹配,返回
list
的副本。 - 注意:这个
replace
函数可以用于更新数组中的元素,而不改变原始数组,因为它总是返回一个新的数组。
- 检查
- 方法流程说明:
select :对数组同时进行过滤和映射,筛选和转换数组中的元素;
- 使用说明
- 参数:对象数组、映射条件函数、过滤条件函数。
- 返回值:过滤完后的映射值组成的数组。
- 使用代码示例
import { select } from 'radash' const fish = [ { name: 'Marlin', weight: 105, source: 'ocean' }, { name: 'Bass', weight: 8, source: 'lake' }, { name: 'Trout', weight: 13, source: 'lake' } ] select( fish, f => f.weight, f => f.source === 'lake' ) // => [8, 13]
- 源码解析
// 定义一个泛型函数 `select`。 export const select = <T, K>( // 第一个参数是一个具有只读属性的泛型数组 `array`。 array: readonly T[], // 第二个参数是一个 `mapper` 函数,它接受一个数组元素和它的索引, // 返回一个新类型 `K` 的值。 mapper: (item: T, index: number) => K, // 第三个参数是一个 `condition` 函数,它也接受一个数组元素和它的索引, // 返回一个布尔值来指示元素是否满足选择条件。 condition: (item: T, index: number) => boolean ) => { // 如果传入的数组 `array` 不存在,则返回一个空数组。 if (!array) return [] // 使用数组的 `reduce` 方法来构建一个新数组,该数组仅包含满足 `condition` 的元素, // 并且这些元素已经通过 `mapper` 函数转换。 return array.reduce((acc, item, index) => { // 使用 `condition` 函数检查当前元素 `item` 是否满足条件。 if (!condition(item, index)) return acc // 如果元素满足条件,则使用 `mapper` 函数对其进行转换,并将结果添加到累加器 `acc`。 acc.push(mapper(item, index)) // 返回更新后的累加器 `acc`。 return acc }, [] as K[]) // 初始化累加器 `acc` 为一个空数组。 }
- 方法流程说明:
- 检查
array
是否存在。如果不存在,返回一个空数组。 - 使用
reduce
方法遍历array
数组。reduce
方法的累加器acc
是一个新数组,用于存储转换后的元素。 - 对于数组中的每个元素,使用
condition
函数检查该元素是否满足选择条件。 - 如果
condition
函数返回true
,则使用mapper
函数对该元素进行转换,并将转换后的结果添加到累加器acc
中。 - 继续处理数组的下一个元素,直到所有元素都被处理完毕。
- 返回累加器
acc
,它现在是一个包含所有已转换且满足条件的元素的新数组。 - 这个
select
函数可以用于在不改变原始数组的情况下,筛选和转换数组中的元素。
- 检查
- 方法流程说明:
shift :把目标数组向右循环移动 n 个位置返回为一个新数组;
- 使用说明
- 参数:目标数组、移动步数。
- 返回值:移动后的新数组。
- 使用代码示例
import { shift } from 'radash' const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] shift(arr, 3) // => [7, 8, 9, 1, 2, 3, 4, 5, 6]
- 源码解析
// 定义一个泛型函数 `select`。 export const select = <T, K>( // 第一个参数是一个具有只读属性的泛型数组 `array`。 array: readonly T[], // 第二个参数是一个 `mapper` 函数,它接受一个数组元素和它的索引, // 返回一个新类型 `K` 的值。 mapper: (item: T, index: number) => K, // 第三个参数是一个 `condition` 函数,它也接受一个数组元素和它的索引, // 返回一个布尔值来指示元素是否满足选择条件。 condition: (item: T, index: number) => boolean ) => { // 如果传入的数组 `array` 不存在,则返回一个空数组。 if (!array) return [] // 使用数组的 `reduce` 方法来构建一个新数组,该数组仅包含满足 `condition` 的元素, // 并且这些元素已经通过 `mapper` 函数转换。 return array.reduce((acc, item, index) => { // 使用 `condition` 函数检查当前元素 `item` 是否满足条件。 if (!condition(item, index)) return acc // 如果元素满足条件,则使用 `mapper` 函数对其进行转换,并将结果添加到累加器 `acc`。 acc.push(mapper(item, index)) // 返回更新后的累加器 `acc`。 return acc }, [] as K[]) // 初始化累加器 `acc` 为一个空数组。 }
- 方法流程说明:
- 检查
array
是否存在。如果不存在,返回一个空数组。 - 使用
reduce
方法遍历array
数组。reduce
方法的累加器acc
是一个新数组,用于存储转换后的元素。 - 对于数组中的每个元素,使用
condition
函数检查该元素是否满足选择条件。 - 如果
condition
函数返回true
,则使用mapper
函数对该元素进行转换,并将转换后的结果添加到累加器acc
中。 - 继续处理数组的下一个元素,直到所有元素都被处理完毕。
- 返回累加器
acc
,它现在是一个包含所有已转换且满足条件的元素的新数组。 - 这个
select
函数可以用于在不改变原始数组的情况下,筛选和转换数组中的元素。
- 检查
- 方法流程说明:
sift:过滤调列表中值为false的项,返回剩余为true的项组成的数组;
- 使用说明
- 参数:数组。
- 返回值:过滤后的新数组。
- 使用代码示例
import { sift } from 'radash' const fish = ['salmon', null, false, NaN, 'sockeye', 'bass'] sift(fish) // => ['salmon', 'sockeye', 'bass']
- 源码解析
// 定义一个泛型函数 `sift`,它接收一个参数 `list`,这是一个包含泛型 `T` 或假值的只读数组。 export const sift = <T>(list: readonly (T | Falsy)[]): T[] => { // 使用 `filter` 方法遍历数组 `list`,移除所有假值。 // `x => !!x` 是一个函数,它将每个元素强制转换为布尔值,然后再次强制转换回来, // 以确保只有真值(Truthy values)被保留。 // `as T[]` 是一个类型断言,它告诉 TypeScript 编译器过滤后的数组只包含类型 `T` 的元素。 return (list?.filter(x => !!x) as T[]) ?? [] }
- 方法流程说明:
- 检查
list
是否存在。如果不存在,返回一个空数组[]
。 - 使用
filter
方法遍历数组list
,对于每个元素x
,使用!!x
将它转换为一个布尔值。这样做的结果是,如果x
是一个假值(如false
、0
、""
、null
、undefined
或NaN
),它将被转换为false
,如果x
是一个真值,它将被转换为true
。 filter
方法根据这个布尔值决定是否保留元素。只有那些转换为true
的元素会被保留在新数组中。- 使用类型断言
as T[]
告诉 TypeScript 编译器,经过过滤后的数组将只包含类型T
的元素。 - 如果
list
是null
或undefined
,list?.filter(x => !!x)
将产生undefined
。这时,通过使用空值合并运算符??
,函数将返回一个空数组[]
。 - 最终返回过滤后的数组,其中仅包含真值元素。
- 检查
- 方法流程说明:
sort :把数组按照条件函数指定的项的数值大小排序,支持升序和降序;
- 使用说明
- 参数:对象数组、指定标识符的条件函数、是否降序(Boolean值,不传为false)。
- 返回值:排序后的数组。
- 使用代码示例
import { sort } from 'radash' const fish = [ { name: 'Marlin', weight: 105 }, { name: 'Bass', weight: 8 }, { name: 'Trout', weight: 13 } ] sort(fish, f => f.weight) // => [bass, trout, marlin] sort(fish, f => f.weight, true) // => [marlin, trout, bass]
- 源码解析
// 定义一个泛型函数 `sort`。 export const sort = <T>( // 第一个参数 `array` 是一个具有只读属性的泛型数组。 array: readonly T[], // 第二个参数 `getter` 是一个函数,它从每个数组元素中提取一个数字,用于比较大小。 getter: (item: T) => number, // 第三个参数 `desc` 是一个布尔值,指定排序方向。默认为 `false`,表示升序排序。 desc = false ) => { // 如果传入的数组 `array` 不存在,则返回一个空数组。 if (!array) return [] // 定义一个升序比较函数 `asc`,它使用 `getter` 函数的返回值进行比较。 const asc = (a: T, b: T) => getter(a) - getter(b) // 定义一个降序比较函数 `dsc`,它也使用 `getter` 函数的返回值进行比较,但顺序相反。 const dsc = (a: T, b: T) => getter(b) - getter(a) // 使用数组的 `slice` 方法创建数组的一个副本,然后使用 `sort` 方法对这个副本进行排序。 // 根据 `desc` 参数的值选择使用 `dsc` 或 `asc` 比较函数。 return array.slice().sort(desc === true ? dsc : asc) }
- 方法流程说明:
- 检查
array
是否存在。如果不存在,返回一个空数组。 - 定义两个比较函数,
asc
用于升序排序,dsc
用于降序排序。这两个函数都使用getter
函数从元素中提取比较值。 - 使用
slice
方法创建数组的一个浅副本,以避免修改原始数组。 - 使用
sort
方法对数组副本进行排序。sort
方法接受一个比较函数,该函数根据desc
参数的值决定使用asc
还是dsc
。 - 返回排序后的数组副本。
- 检查
- 方法流程说明:
sum:数组对象根据条件函数指定想的数组求和;
-
使用说明
- 参数:对象数组、指定标识符的条件函数。
- 返回值:符合条件的项的和。
-
使用代码示例
import { sum } from 'radash' const fish = [ { name: 'Marlin', weight: 100 }, { name: 'Bass', weight: 10 }, { name: 'Trout', weight: 15 } ] sum(fish, f => f.weight) // => 125
-
源码解析
// 这是 `sum` 函数的第一种声明,用于处理数组直接包含数字的情况。 export function sum<T extends number>(array: readonly T[]): number // 这是 `sum` 函数的第二种声明,用于处理数组包含对象的情况。 // 需要一个额外的函数 `fn` 来从每个对象中提取数值。 export function sum<T extends object>( array: readonly T[], fn: (item: T) => number ): number // 这是 `sum` 函数的实现,它可以处理上述两种情况。 export function sum<T extends object | number>( array: readonly any[], fn?: (item: T) => number ): number { // 使用数组的 `reduce` 方法来累积所有元素的总和。 // 如果数组不存在,使用空数组 `[]` 作为默认值。 // `reduce` 的回调函数将累加器 `acc` 和当前元素 `item` 相加。 // 如果提供了函数 `fn`,则使用 `fn(item)` 获取数值;如果没有提供 `fn`,则直接使用 `item`。 return (array || []).reduce((acc, item) => acc + (fn ? fn(item) : item), 0) }
- 方法流程说明:
- 检查
array
是否存在。如果不存在,使用空数组[]
作为默认值。 - 使用
reduce
方法遍历数组。reduce
方法的累加器acc
初始化为0。 - 对于数组中的每个元素
item
,如果提供了函数fn
,则调用fn(item)
来获取元素的数值;如果没有提供fn
,则直接使用item
作为数值。 - 将每个元素的数值累加到
acc
上。 - 返回累加器
acc
的最终值,即数组中所有元素的总和。
- 检查
- 方法流程说明:
toggle:查找数组中是否有我们给定的项,有则删除,没有则添加;
- 使用说明
- 参数:目标数组、条件值(可以是条件函数)、在前面添加还是在后边添加。
- 返回值:处理后的新数组。
- 使用代码示例
import { toggle } from 'radash' // 基本用法 const gods = ['ra', 'zeus', 'loki'] toggle(gods, 'ra') // => [zeus, loki] toggle(gods, 'vishnu') // => [ra, zeus, loki, vishnu] // 切换(数组、条件项、指定标识符的条件函数) import { toggle } from 'radash' const ra = { name: 'Ra' } const zeus = { name: 'Zeus' } const loki = { name: 'Loki' } const vishnu = { name: 'Vishnu' } const gods = [ra, zeus, loki] toggle(gods, ra, g => g.name) // => [zeus, loki] toggle(gods, vishnu, g => g.name) // => [ra, zeus, loki, vishnu] // 切换(数组、条件项、条件函数,覆盖项) import { toggle } from 'radash' const gods = ['ra', 'zeus', 'loki'] toggle(gods, 'vishnu', g => g, { strategy: 'prepend' }) // => [vishnu, ra, zeus, loki]
- 源码解析
// 定义一个泛型函数 `toggle`。 export const toggle = <T>( // 第一个参数 `list` 是一个具有只读属性的泛型数组。 list: readonly T[], // 第二个参数 `item` 是一个类型为 `T` 的元素,它将被切换(添加或移除)。 item: T, // 第三个可选参数 `toKey` 是一个函数,用于将元素转换为可比较的键。 toKey?: null | ((item: T, idx: number) => number | string | symbol), // 第四个可选参数 `options` 是一个对象,允许指定添加元素的策略(追加或前置)。 options?: { strategy?: 'prepend' | 'append' } ) => { // 如果 `list` 和 `item` 都不存在或为空,则返回一个空数组。 if (!list && !item) return [] // 如果 `list` 不存在或为空,返回一个只包含 `item` 的数组。 if (!list) return [item] // 如果 `item` 不存在或为空,返回 `list` 数组的副本。 if (!item) return [...list] // 定义一个 `matcher` 函数,用于检查元素是否与 `item` 匹配。 // 如果提供了 `toKey` 函数,则使用它来获取比较的键;如果没有,则直接比较元素。 const matcher = toKey ? (x: T, idx: number) => toKey(x, idx) === toKey(item, idx) : (x: T) => x === item // 使用 `find` 方法检查 `list` 中是否存在与 `item` 匹配的元素。 const existing = list.find(matcher) // 如果存在匹配的元素,使用 `filter` 方法移除它。 if (existing) return list.filter((x, idx) => !matcher(x, idx)) // 根据 `options` 中的 `strategy` 决定是追加还是前置新元素。 const strategy = options?.strategy ?? 'append' // 如果策略是 'append',将 `item` 追加到 `list` 的末尾。 if (strategy === 'append') return [...list, item] // 否则,将 `item` 前置到 `list` 的开头。 return [item, ...list] }
- 方法流程说明:
- 进行一系列的检查,如果
list
和item
都不存在或为空,或者只有list
不存在或为空,或者只有item
不存在或为空,则返回相应的结果。 - 定义
matcher
函数,用于检查元素是否与item
匹配。如果存在toKey
函数,则使用它来比较转换后的键。 - 检查
list
中是否存在与item
匹配的元素。 - 如果存在匹配的元素,使用
filter
方法创建一个新数组,其中不包含匹配的元素。 - 如果不存在匹配的元素,根据
options
中的strategy
决定是将item
追加到数组末尾还是添加到数组开头,并返回新数组。
- 进行一系列的检查,如果
- 方法流程说明:
unique:数组去重,去除数组中重复的项;
-
使用说明
- 参数:目标数组、指定唯一标识符的条件函数。
- 返回值:去重后的数组。
-
使用代码示例
import { unique } from 'radash' const fish = [ { name: 'Marlin', weight: 105, source: 'ocean' }, { name: 'Salmon', weight: 22, source: 'river' }, { name: 'Salmon', weight: 22, source: 'river' } ] unique( fish, f => f.name ) // [ // { name: 'Marlin', weight: 105, source: 'ocean' }, // { name: 'Salmon', weight: 22, source: 'river' } // ] ``
-
源码解析
// 定义一个泛型函数 `unique`。 export const unique = <T, K extends string | number | symbol>( // 第一个参数 `array` 是一个具有只读属性的泛型数组。 array: readonly T[], // 第二个可选参数 `toKey` 是一个函数,用于将数组元素转换为可比较的键。 toKey?: (item: T) => K ): T[] => { // 使用数组的 `reduce` 方法来构建一个记录对象 `valueMap`,该对象的键是元素的键,值是元素本身。 const valueMap = array.reduce((acc, item) => { // 使用 `toKey` 函数获取元素的键。如果 `toKey` 未提供,直接将元素作为键。 const key = toKey ? toKey(item) : (item as any as string | number | symbol) // 如果 `key` 已存在于 `acc` 中,表示该元素已经出现过,跳过它。 if (acc[key]) return acc // 如果 `key` 不存在,将元素添加到 `acc` 中。 acc[key] = item // 返回更新后的累加器 `acc`。 return acc }, {} as Record<string | number | symbol, T>) // 使用 `Object.values` 方法从 `valueMap` 中提取所有的值,这些值是唯一的元素。 return Object.values(valueMap) }
- 方法流程说明:
- 使用
reduce
方法遍历array
数组,构建一个记录对象valueMap
。 - 对于数组中的每个元素
item
,如果提供了toKey
函数,则调用它来获取元素的键;如果没有提供toKey
函数,则直接使用元素本身作为键。 - 检查键是否已经存在于
valueMap
中。如果存在,这意味着元素已经在之前出现过,因此跳过它。 - 如果键不存在,将元素添加到
valueMap
中,使用键作为索引。 - 继续处理数组的下一个元素,直到所有元素都被处理完毕。
- 使用
Object.values
方法提取valueMap
中的所有值,因为valueMap
中的键是唯一的,所以这些值也是唯一的。 - 返回包含所有唯一元素的新数组。
- 使用
- 方法流程说明:
zipToObject:将第一个数组中的键映射到第二个数组中对应的值;
- 使用说明
- 参数:数组1、数组2(或者是传入一个函数)。
- 返回值:映射后的对象。
- 使用代码示例
import { zipToObject } from 'radash' const names = ['ra', 'zeus', 'loki'] const cultures = ['egypt', 'greek', 'norse'] zipToObject(names, cultures) // => { ra: egypt, zeus: greek, loki: norse } zipToObject(names, (k, i) => k + i) // => { ra: ra0, zeus: zeus1, loki: loki2 } zipToObject(names, null) // => { ra: null, zeus: null, loki: null }
- 源码解析
// 定义一个泛型函数 `zipToObject`。 export function zipToObject<K extends string | number | symbol, V>( // 第一个参数 `keys` 是一个数组,包含类型为 `K` 的键。 keys: K[], // 第二个参数 `values` 可以是一个单一的值,一个返回值的函数,或者一个值的数组。 values: V | ((key: K, idx: number) => V) | V[] ): Record<K, V> { // 如果 `keys` 不存在或 `keys` 的长度为0,返回一个空对象。 if (!keys || !keys.length) { return {} as Record<K, V> } // ...(函数的其余部分) }
- 方法流程说明:
-
首先检查
keys
数组是否存在且长度不为零。如果keys
不存在或为空数组,则函数返回一个空对象。 -
如果
keys
存在且不为空,函数将继续执行(这部分代码在这段代码之外)。 -
相关说明:
- 这个函数预期的行为是,对于
keys
数组中的每个键,它会从values
中取出相应的值(或者使用提供的函数计算出的值),并创建一个对象,其中每个键都映射到一个值。 - 由于代码片段没有提供函数的完整实现,我们不能确定函数如何处理
values
参数。如果values
是一个数组,它可能会将keys
数组中的每个键与values
数组中的相应值关联起来。如果values
是一个函数,它可能会对每个键调用这个函数来生成值。如果values
是单一的值,它可能会将这个值分配给keys
数组中的每个键。 - 这个函数的返回类型是
Record<K, V>
,这是 TypeScript 中的一个工具类型,表示一个对象,其中键的类型为K
,值的类型为V
。在这个上下文中,K
被限制为string
、number
或symbol
类型,这是对象键的有效类型。
- 这个函数预期的行为是,对于
-
- 方法流程说明:
zip:把两个数组变为二维数组,二维数组中的每个数组包含两个项分别为两个传入数组的相同位置的项。
- 使用说明
- 参数:数组1、数组2.
- 返回值:映射后的二维数组。
- 使用代码示例
import { zip } from 'radash' const names = ['ra', 'zeus', 'loki'] const cultures = ['egypt', 'greek', 'norse'] zip(names, cultures) // => [ // [ra, egypt] // [zeus, greek] // [loki, norse] // ]
- 源码解析
// 这是 `zip` 函数的实现部分,它使用剩余参数 `...arrays` 来接收任意数量的数组。 export function zip<T>(...arrays: T[][]): T[][] { // 检查传入的 `arrays` 是否存在且长度不为零,如果不是,则返回一个空数组。 if (!arrays || !arrays.length) return [] // 使用 `Math.max` 和 `map` 方法找出传入数组中长度最大的值。 return new Array(Math.max(...arrays.map(({ length }) => length))) // 使用 `fill` 方法填充一个新数组,初始填充值为 `[]`(空数组)。 .fill([]) // 使用 `map` 方法遍历新数组的每个位置 `idx`。 .map((_, idx) => arrays.map(array => array[idx])) // 对于每个位置 `idx`,从 `arrays` 中的每个数组 `array` 获取 `idx` 位置的元素。 // 这样就组合成了一个元组,包含来自每个数组的元素。 }
- 方法流程说明:
- 检查是否有数组传入,如果没有,则返回空数组。
- 创建一个新数组,长度是传入数组中最长的那个数组的长度。
- 遍历新数组的每个索引位置
idx
。 - 对于每个位置
idx
,遍历传入的所有数组,并从每个数组中取出该索引位置的元素。 - 将所有取出的元素组成一个元组,并将这个元组放入新数组的相应位置。
- 返回这个新数组,它包含了所有组合的元组。
- 方法流程说明:
写在后面
- 等所有方法更新完毕,作者会整理一份
radash
完整方法目录上传,包括思维导图和使用目录。 - 下期我们会整理分享Array方法的使用说明目录,同样包括思维导图。
- 大家有任何问题或见解,欢迎评论区留言交流和批评指正!!!
- 你的每一个收藏都是作者写作的动力!!!
- 点击访问:radash官网