-
-
Notifications
You must be signed in to change notification settings - Fork 51
/
array.ts
188 lines (168 loc) · 4.74 KB
/
array.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import { clamp } from './math'
import type { Arrayable, Nullable } from './types'
/**
* Convert `Arrayable<T>` to `Array<T>`
*
* @category Array
*/
export function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T> {
array = array ?? []
return Array.isArray(array) ? array : [array]
}
/**
* Convert `Arrayable<T>` to `Array<T>` and flatten it
*
* @category Array
*/
export function flattenArrayable<T>(array?: Nullable<Arrayable<T | Array<T>>>): Array<T> {
return toArray(array).flat(1) as Array<T>
}
/**
* Use rest arguments to merge arrays
*
* @category Array
*/
export function mergeArrayable<T>(...args: Nullable<Arrayable<T>>[]): Array<T> {
return args.flatMap(i => toArray(i))
}
export type PartitionFilter<T> = (i: T, idx: number, arr: readonly T[]) => any
/**
* Divide an array into two parts by a filter function
*
* @category Array
* @example const [odd, even] = partition([1, 2, 3, 4], i => i % 2 != 0)
*/
export function partition<T>(array: readonly T[], f1: PartitionFilter<T>): [T[], T[]]
export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>): [T[], T[], T[]]
export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>): [T[], T[], T[], T[]]
export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>): [T[], T[], T[], T[], T[]]
export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[]]
export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>, f6: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[], T[]]
export function partition<T>(array: readonly T[], ...filters: PartitionFilter<T>[]): any {
const result: T[][] = new Array(filters.length + 1).fill(null).map(() => [])
array.forEach((e, idx, arr) => {
let i = 0
for (const filter of filters) {
if (filter(e, idx, arr)) {
result[i].push(e)
return
}
i += 1
}
result[i].push(e)
})
return result
}
/**
* Unique an Array
*
* @category Array
*/
export function uniq<T>(array: readonly T[]): T[] {
return Array.from(new Set(array))
}
/**
* Get last item
*
* @category Array
*/
export function last(array: readonly []): undefined
export function last<T>(array: readonly T[]): T
export function last<T>(array: readonly T[]): T | undefined {
return at(array, -1)
}
/**
* Remove an item from Array
*
* @category Array
*/
export function remove<T>(array: T[], value: T) {
if (!array)
return false
const index = array.indexOf(value)
if (index >= 0) {
array.splice(index, 1)
return true
}
return false
}
/**
* Get nth item of Array. Negative for backward
*
* @category Array
*/
export function at(array: readonly [], index: number): undefined
export function at<T>(array: readonly T[], index: number): T
export function at<T>(array: readonly T[] | [], index: number): T | undefined {
const len = array.length
if (!len)
return undefined
if (index < 0)
index += len
return array[index]
}
/**
* Genrate a range array of numbers. The `stop` is exclusive.
*
* @category Array
*/
export function range(stop: number): number[]
export function range(start: number, stop: number, step?: number): number[]
export function range(...args: any): number[] {
let start: number, stop: number, step: number
if (args.length === 1) {
start = 0
step = 1;
([stop] = args)
}
else {
([start, stop, step = 1] = args)
}
const arr: number[] = []
let current = start
while (current < stop) {
arr.push(current)
current += step || 1
}
return arr
}
/**
* Move element in an Array
*
* @category Array
* @param arr
* @param from
* @param to
*/
export function move<T>(arr: T[], from: number, to: number) {
arr.splice(to, 0, arr.splice(from, 1)[0])
return arr
}
/**
* Clamp a number to the index ranage of an array
*
* @category Array
*/
export function clampArrayRange(n: number, arr: readonly unknown[]) {
return clamp(n, 0, arr.length - 1)
}
/**
* Get random items from an array
*
* @category Array
*/
export function sample<T>(arr: T[], count: number) {
return Array.from({ length: count }, _ => arr[Math.round(Math.random() * (arr.length - 1))])
}
/**
* Shuffle an array. This function mutates the array.
*
* @category Array
*/
export function shuffle<T>(array: T[]): T[] {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]]
}
return array
}