tips:点赞 + 收藏 = 学会!
radash
的相关信息和部分Array相关方法,详情可前往主页查看。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
函数可以用于更新数组中的元素,而不改变原始数组,因为它总是返回一个新的数组。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
函数可以用于在不改变原始数组的情况下,筛选和转换数组中的元素。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
函数可以用于在不改变原始数组的情况下,筛选和转换数组中的元素。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
。这时,通过使用空值合并运算符 ??
,函数将返回一个空数组 []
。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
。使用说明
使用代码示例
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
的最终值,即数组中所有元素的总和。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
追加到数组末尾还是添加到数组开头,并返回新数组。使用说明
使用代码示例
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
中的键是唯一的,所以这些值也是唯一的。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
类型,这是对象键的有效类型。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
完整方法目录上传,包括思维导图和使用目录。或许你最近在某个地方听过或者看过 `radash` 这个词,它是一个typescript编写的方法库,无论你是想简单使用还是深入了解他的源码,本系列文章都值得一读。
目前为止,radash库的所有方法我们已经分享完毕。如果你想尝试使用,又或者想了解下源码,阿瓜的文章都值得一读,相信你总能有所收获。后续我们回整理一份使用说明进行发布。