@stacksjs/arrays 0.58.47 → 0.58.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/arrays",
3
3
  "type": "module",
4
- "version": "0.58.47",
4
+ "version": "0.58.49",
5
5
  "description": "The Stacks array utilities.",
6
6
  "author": "Chris Breuer",
7
7
  "license": "MIT",
@@ -40,7 +40,8 @@
40
40
  ],
41
41
  "files": [
42
42
  "README.md",
43
- "dist"
43
+ "dist",
44
+ "src"
44
45
  ],
45
46
  "scripts": {
46
47
  "build": "bun --bun build.ts",
@@ -48,12 +49,12 @@
48
49
  "prepublishOnly": "bun --bun run build"
49
50
  },
50
51
  "peerDependencies": {
51
- "@stacksjs/utils": "workspace:*"
52
+ "@stacksjs/utils": "latest"
52
53
  },
53
54
  "dependencies": {
54
55
  "@stacksjs/utils": "latest"
55
56
  },
56
57
  "devDependencies": {
57
- "@stacksjs/development": "workspace:*"
58
+ "@stacksjs/development": "latest"
58
59
  }
59
60
  }
package/src/arr.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './helpers'
2
+ export * from './math'
3
+ export * from './contains'
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Returns true if the needle is contained in the haystack.
3
+ *
4
+ * @param needle
5
+ * @param haystack
6
+ * @example
7
+ * ```ts
8
+ * contains('foo', ['foo', 'bar']) // true
9
+ * contains('foo', ['bar']) // false
10
+ * ```
11
+ */
12
+ export function contains(needle: string, haystack: string[]) {
13
+ return haystack.some(hay => needle.includes(hay))
14
+ }
15
+
16
+ /**
17
+ * Returns true if all needles are contained in the haystack.
18
+ * @param needles
19
+ * @param haystack
20
+ * @example
21
+ * ```ts
22
+ * containsAll(['foo', 'bar'], ['foo', 'bar', 'baz']) // true
23
+ * containsAll(['foo', 'bar'], ['foo', 'baz']) // false
24
+ * ```
25
+ */
26
+ export function containsAll(needles: string[], haystack: string[]) {
27
+ return needles.every(needle => contains(needle, haystack))
28
+ }
29
+
30
+ /**
31
+ * Returns true if any needle is contained in the haystack.
32
+ * @param needles
33
+ * @param haystack
34
+ * @example
35
+ * ```ts
36
+ * containsAny(['foo', 'bar'], ['foo', 'bar', 'baz']) // true
37
+ * containsAny(['foo', 'bar'], ['foo', 'baz']) // true
38
+ * containsAny(['foo', 'bar'], ['baz']) // false
39
+ * ```
40
+ */
41
+ export function containsAny(needles: string[], haystack: string[]) {
42
+ return needles.some(needle => contains(needle, haystack))
43
+ }
44
+
45
+ /**
46
+ * Returns true if none of the needles are contained in the haystack.
47
+ * @param needles
48
+ * @param haystack
49
+ * @example
50
+ * ```ts
51
+ * containsNone(['foo', 'bar'], ['foo', 'bar', 'baz']) // false
52
+ * containsNone(['foo', 'bar'], ['foo', 'baz']) // false
53
+ * containsNone(['foo', 'bar'], ['baz']) // true
54
+ * ```
55
+ */
56
+ export function containsNone(needles: string[], haystack: string[]) {
57
+ return !containsAny(needles, haystack)
58
+ }
59
+
60
+ /**
61
+ * Returns true if all needles are contained in the haystack.
62
+ * @param needles
63
+ * @param haystack
64
+ * @example
65
+ * ```ts
66
+ * containsOnly(['foo', 'bar'], ['foo', 'bar', 'baz']) // false
67
+ * containsOnly(['foo', 'bar'], ['foo', 'baz']) // false
68
+ * containsOnly(['foo', 'bar'], ['foo', 'bar']) // true
69
+ * ```
70
+ */
71
+ export function containsOnly(needles: string[], haystack: string[]) {
72
+ return containsAll(haystack, needles)
73
+ }
74
+
75
+ /**
76
+ * Returns true if the needle is not contained in the haystack.
77
+ * @param needle
78
+ * @param haystack
79
+ * @example
80
+ * ```ts
81
+ * doesNotContain('foo', ['foo', 'bar']) // false
82
+ * doesNotContain('foo', ['bar']) // true
83
+ * ```
84
+ */
85
+ export function doesNotContain(needle: string, haystack: string[]) {
86
+ return !contains(needle, haystack)
87
+ }
package/src/helpers.ts ADDED
@@ -0,0 +1,272 @@
1
+ import type { Arrayable, Nullable } from '@stacksjs/types'
2
+ import { clamp } from '@stacksjs/utils'
3
+
4
+ /**
5
+ * Convert `Arrayable<T>` to `Array<T>`
6
+ *
7
+ * @category Array
8
+ * @example
9
+ * ```ts
10
+ * toArray('foo') // ['foo']
11
+ * toArray(['foo']) // ['foo']
12
+ * toArray(null) // []
13
+ * toArray(undefined) // []
14
+ * toArray(1) // [1]
15
+ * toArray([1]) // [1]
16
+ * toArray({ foo: 'bar' }) // [{ foo: 'bar' }]
17
+ * toArray([{ foo: 'bar' }]) // [{ foo: 'bar' }]
18
+ * ```
19
+ */
20
+ export function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T> {
21
+ array = array ?? []
22
+ return Array.isArray(array) ? array : [array]
23
+ }
24
+
25
+ /**
26
+ * Flatten `Arrayable<T>` to `Array<T>`
27
+ *
28
+ * @category Array
29
+ * @example
30
+ * ```ts
31
+ * flatten([1, [2, [3, [4, [5]]]]]) // [1, 2, 3, 4, 5]
32
+ * ```
33
+ */
34
+ export function flatten<T>(array?: Nullable<Arrayable<T | Array<T>>>): Array<T> {
35
+ return toArray(array).flat(1) as Array<T>
36
+ }
37
+
38
+ /**
39
+ * Use rest arguments to merge arrays
40
+ *
41
+ * @category Array
42
+ * @example
43
+ * ```ts
44
+ * mergeArrayable([1, 2], [3, 4], [5, 6]) // [1, 2, 3, 4, 5, 6]
45
+ * ```
46
+ */
47
+ export function mergeArrayable<T>(...args: Nullable<Arrayable<T>>[]): Array<T> {
48
+ return args.flatMap(i => toArray(i))
49
+ }
50
+
51
+ export type PartitionFilter<T> = (i: T, idx: number, arr: readonly T[]) => any
52
+
53
+ /**
54
+ * Divide an array into two parts by a filter function
55
+ *
56
+ * @category Array
57
+ * @example
58
+ * ```ts
59
+ * const [odd, even] = partition([1, 2, 3, 4], i => i % 2 != 0)
60
+ * console.log(odd) // [1, 3]
61
+ * console.log(even) // [2, 4]
62
+ * ```
63
+ */
64
+ export function partition<T>(array: readonly T[], f1: PartitionFilter<T>): [T[], T[]]
65
+ export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>): [T[], T[], T[]]
66
+ export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>): [T[], T[], T[], T[]]
67
+ export function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>): [T[], T[], T[], T[], T[]]
68
+ 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[]]
69
+ 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[]]
70
+ export function partition<T>(array: readonly T[], ...filters: PartitionFilter<T>[]): any {
71
+ const result: T[][] = Array.from({ length: filters.length + 1 }).fill(null).map(() => [])
72
+
73
+ array.forEach((e, idx, arr) => {
74
+ let i = 0
75
+ for (const filter of filters) {
76
+ if (filter(e, idx, arr)) {
77
+ result[i].push(e)
78
+ return
79
+ }
80
+ i += 1
81
+ }
82
+ result[i].push(e)
83
+ })
84
+ return result
85
+ }
86
+
87
+ /**
88
+ * Unique an Array
89
+ *
90
+ * @category Array
91
+ * @example
92
+ * ```ts
93
+ * uniq([1, 2, 3, 3, 2, 1]) // [1, 2, 3]
94
+ * ```
95
+ */
96
+ export function uniq<T>(array: readonly T[]): T[] {
97
+ return Array.from(new Set(array))
98
+ }
99
+
100
+ /**
101
+ * Unique an Array
102
+ *
103
+ * @param array
104
+ * @example
105
+ * ```ts
106
+ * unique([1, 2, 3, 3, 2, 1]) // [1, 2, 3]
107
+ * ```
108
+ */
109
+ export function unique<T>(array: readonly T[]): T[] {
110
+ return uniq(array)
111
+ }
112
+
113
+ /**
114
+ * Unique an Array by a custom equality function
115
+ *
116
+ * @category Array
117
+ * @example
118
+ * ```ts
119
+ * uniqueBy([1, 2, 3, 3, 2, 1], (a, b) => a === b) // [1, 2, 3]
120
+ * ```
121
+ */
122
+ export function uniqueBy<T>(array: readonly T[], equalFn: (a: any, b: any) => boolean): T[] {
123
+ return array.reduce((acc: T[], cur: any) => {
124
+ const index = acc.findIndex((item: any) => equalFn(cur, item))
125
+ if (index === -1)
126
+ acc.push(cur)
127
+ return acc
128
+ }, [])
129
+ }
130
+
131
+ /**
132
+ * Get last item
133
+ *
134
+ * @category Array
135
+ * @example
136
+ * ```ts
137
+ * last([1, 2, 3]) // 3
138
+ * ```
139
+ */
140
+ export function last(array: readonly []): undefined
141
+ export function last<T>(array: readonly T[]): T
142
+ export function last<T>(array: readonly T[]): T | undefined {
143
+ return at(array, -1)
144
+ }
145
+
146
+ /**
147
+ * Remove an item from Array
148
+ *
149
+ * @category Array
150
+ * @example
151
+ * ```ts
152
+ * const arr = [1, 2, 3]
153
+ * remove(arr, 2) // true
154
+ * Arr.remove(arr, 4) // false
155
+ * console.log(arr) // [1, 3]
156
+ */
157
+ export function remove<T>(array: T[], value: T) {
158
+ if (!array)
159
+ return false
160
+
161
+ const index = array.indexOf(value)
162
+ if (index >= 0) {
163
+ array.splice(index, 1)
164
+ return true
165
+ }
166
+
167
+ return false
168
+ }
169
+
170
+ /**
171
+ * Get nth item of Array. Negative for backward
172
+ *
173
+ * @category Array
174
+ * @example
175
+ * ```ts
176
+ * at([1, 2, 3], 1) // 2
177
+ * at([1, 2, 3], -1) // 3
178
+ * at([1, 2, 3], 3) // undefined
179
+ * at([1, 2, 3], -4) // undefined
180
+ * ```
181
+ */
182
+ export function at(array: readonly [], index: number): undefined
183
+ export function at<T>(array: readonly T[], index: number): T
184
+ export function at<T>(array: readonly T[] | [], index: number): T | undefined {
185
+ const len = array.length
186
+ if (!len)
187
+ return undefined
188
+
189
+ if (index < 0)
190
+ index += len
191
+
192
+ return array[index]
193
+ }
194
+
195
+ /**
196
+ * Move an item from one index to another
197
+ * @param array
198
+ * @param from
199
+ * @param to
200
+ *
201
+ * @category Array
202
+ * @example
203
+ * ```ts
204
+ * move([1, 2, 3, 4], 0, 2) // [2, 3, 1, 4]
205
+ * move([1, 2, 3, 4], 0, -1) // [2, 3, 4, 1]
206
+ * move([1, 2, 3, 4], -1, 0) // [4, 1, 2, 3]
207
+ * move([1, 2, 3, 4], -1, -2) // [1, 4, 2, 3]
208
+ * move([1, 2, 3, 4], 1, 1) // [1, 2, 3, 4]
209
+ * ```
210
+ */
211
+ export function move<T>(array: T[], from: number, to: number): T[] {
212
+ const len = array.length
213
+ if (!len)
214
+ return []
215
+
216
+ if (from < 0)
217
+ from += len
218
+
219
+ if (to < 0)
220
+ to += len
221
+
222
+ const item = array.splice(from, 1)[0]
223
+ array.splice(to, 0, item)
224
+ return array
225
+ }
226
+
227
+ /**
228
+ * Clamp a number to the index range of an array.
229
+ *
230
+ * @category Array
231
+ * @example
232
+ * ```ts
233
+ * clampArrayRange([1, 2, 3], 0) // 0
234
+ * clampArrayRange([1, 2, 3], 1) // 1
235
+ * clampArrayRange([1, 2, 3], 2) // 2
236
+ * clampArrayRange([1, 2, 3], 3) // 2
237
+ * clampArrayRange([1, 2, 3], 4) // 2
238
+ * clampArrayRange([1, 2, 3], -1) // 0
239
+ */
240
+ export function clampArrayRange(arr: readonly unknown[], n: number) {
241
+ return clamp(n, 0, arr.length - 1)
242
+ }
243
+
244
+ /**
245
+ * Get random items from an array
246
+ *
247
+ * @category Array
248
+ * @example
249
+ * ```ts
250
+ * sample([1, 2, 3, 4], 2) // [2, 3]
251
+ * ```
252
+ */
253
+ export function sample<T>(arr: T[], count: number) {
254
+ return Array.from({ length: count }, _ => arr[Math.round(Math.random() * (arr.length - 1))])
255
+ }
256
+
257
+ /**
258
+ * Shuffle an array. This function mutates the array.
259
+ *
260
+ * @category Array
261
+ * @example
262
+ * ```ts
263
+ * shuffle([1, 2, 3, 4]) // [2, 4, 1, 3]
264
+ * ```
265
+ */
266
+ export function shuffle<T>(array: T[]): T[] {
267
+ for (let i = array.length - 1; i > 0; i--) {
268
+ const j = Math.floor(Math.random() * (i + 1));
269
+ [array[i], array[j]] = [array[j], array[i]]
270
+ }
271
+ return array
272
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './arr'
2
+ export * as arr from './arr'
3
+ export * from './macro'
package/src/macro.ts ADDED
@@ -0,0 +1,131 @@
1
+ import type { Arrayable, Nullable } from '@stacksjs/types'
2
+ import type { PartitionFilter } from './helpers'
3
+ import { at, clampArrayRange, flatten, last, mergeArrayable, move, partition, remove, sample, shuffle, toArray, uniq, uniqueBy } from './helpers'
4
+ import { average, median, mode, range, sum } from './math'
5
+ import { contains, containsAll, containsAny, containsNone, containsOnly, doesNotContain } from './contains'
6
+
7
+ export const Arr = {
8
+ contains(needle: string, haystack: string[]) {
9
+ return contains(needle, haystack)
10
+ },
11
+
12
+ containsAll(needles: string[], haystack: string[]) {
13
+ return containsAll(needles, haystack)
14
+ },
15
+
16
+ containsAny(needles: string[], haystack: string[]) {
17
+ return containsAny(needles, haystack)
18
+ },
19
+
20
+ containsNone(needles: string[], haystack: string[]) {
21
+ return containsNone(needles, haystack)
22
+ },
23
+
24
+ containsOnly(needles: string[], haystack: string[]) {
25
+ return containsOnly(needles, haystack)
26
+ },
27
+
28
+ doesNotContain(needle: string, haystack: string[]) {
29
+ return doesNotContain(needle, haystack)
30
+ },
31
+
32
+ toArray<T>(array?: Nullable<Arrayable<T>>): Array<T> {
33
+ return toArray(array)
34
+ },
35
+
36
+ flatten<T>(array?: Nullable<Arrayable<T | Array<T>>>): Array<T> {
37
+ return flatten(array)
38
+ },
39
+
40
+ mergeArrayable<T>(...args: Nullable<Arrayable<T>>[]): Array<T> {
41
+ return mergeArrayable(...args)
42
+ },
43
+
44
+ partition<T>(array: readonly T[], filter: PartitionFilter<T>): [T[], T[]] {
45
+ return partition(array, filter)
46
+ },
47
+
48
+ /**
49
+ * Returns a random item/s from the array
50
+ */
51
+ random<T>(arr: T[], count = 1): T[] {
52
+ return sample(arr, count)
53
+ },
54
+
55
+ /**
56
+ * Returns random item/s from the array
57
+ */
58
+ sample<T>(arr: T[], count = 1): T[] {
59
+ return sample(arr, count)
60
+ },
61
+
62
+ unique<T>(arr: T[]): T[] {
63
+ return uniq(arr)
64
+ },
65
+
66
+ uniqueBy<T>(arr: readonly T[], equalFn: (a: any, b: any) => boolean): T[] {
67
+ return uniqueBy(arr, equalFn)
68
+ },
69
+
70
+ last<T>(arr: T[]): T | undefined {
71
+ return last(arr)
72
+ },
73
+
74
+ remove<T>(arr: T[], value: T) {
75
+ return remove(arr, value)
76
+ },
77
+
78
+ at(arr: any[], index: number): any {
79
+ return at(arr, index)
80
+ },
81
+
82
+ range(arr: readonly number[]): number {
83
+ return range(arr)
84
+ },
85
+
86
+ move<T>(arr: T[], from: number, to: number): T[] {
87
+ return move(arr, from, to)
88
+ },
89
+
90
+ clampArrayRange(arr: readonly unknown[], n: number) {
91
+ return clampArrayRange(arr, n)
92
+ },
93
+
94
+ shuffle<T>(arr: T[]): T[] {
95
+ return shuffle(arr)
96
+ },
97
+
98
+ /**
99
+ * Returns the sum of all items in the array
100
+ */
101
+ sum(arr: number[]): number {
102
+ return sum(arr)
103
+ },
104
+
105
+ /**
106
+ * Returns the average of all items in the array
107
+ */
108
+ average(arr: number[]): number {
109
+ return average(arr)
110
+ },
111
+
112
+ avg(arr: number[]): number {
113
+ return average(arr)
114
+ },
115
+
116
+ /**
117
+ * Returns the median of all items in the array
118
+ */
119
+ median(arr: number[]): number {
120
+ return median(arr)
121
+ },
122
+
123
+ /**
124
+ * Returns the mode of all items in the array
125
+ */
126
+ mode(arr: number[]): number {
127
+ return mode(arr)
128
+ },
129
+ }
130
+
131
+ export const arr = Arr
package/src/math.ts ADDED
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Returns the average of an array of numbers
3
+ * @param arr
4
+ * @category Array
5
+ * @example
6
+ * ```ts
7
+ * average([1, 2, 3, 4]) // 2.5
8
+ * ```
9
+ */
10
+ export function average(arr: number[]): number {
11
+ return sum(arr) / arr.length
12
+ }
13
+
14
+ /**
15
+ * Returns the average of an array of numbers
16
+ * @param arr
17
+ * @category Array
18
+ * @example
19
+ * ```ts
20
+ * avg([1, 2, 3, 4]) // 2.5
21
+ * ```
22
+ */
23
+ export function avg(arr: number[]): number {
24
+ return average(arr)
25
+ }
26
+
27
+ /**
28
+ * Returns the median of an array of numbers
29
+ * @param arr
30
+ * @category Array
31
+ * @example
32
+ * ```ts
33
+ * median([1, 2, 3, 4]) // 2.5
34
+ * ```
35
+ */
36
+ export function median(arr: number[]): number {
37
+ return arr[Math.floor(arr.length / 2)]
38
+ }
39
+
40
+ /**
41
+ * Returns the mode of an array of numbers
42
+ * @param arr
43
+ * @category Array
44
+ * @example
45
+ * ```ts
46
+ * mode([1, 2, 3, 4]) // 1
47
+ * mode([1, 2, 2, 3, 4]) // 2
48
+ * mode([1, 2, 2, 3, 3, 4]) // 2
49
+ * mode([1, 2, 2, 3, 3, 4, 4]) // 2
50
+ * mode([1, 2, 2, 3, 3, 4, 4, 4]) // 4
51
+ * ```
52
+ */
53
+ export function mode(arr: number[]): number {
54
+ return arr.sort((a, b) => arr.filter(v => v === a).length - arr.filter(v => v === b).length).pop()!
55
+ }
56
+
57
+ /**
58
+ * Returns the sum of an array of numbers
59
+ * @param array
60
+ * @category Array
61
+ * @example
62
+ * ```ts
63
+ * sum([1, 2, 3, 4]) // 10
64
+ * ```
65
+ */
66
+ export function sum(array: readonly number[]): number {
67
+ return array.reduce((acc, cur) => acc + cur, 0)
68
+ }
69
+
70
+ /**
71
+ * Returns the product of an array of numbers
72
+ * @param array
73
+ * @category Array
74
+ * @example
75
+ * ```ts
76
+ * product([1, 2, 3, 4]) // 24
77
+ * ```
78
+ */
79
+ export function product(array: readonly number[]): number {
80
+ return array.reduce((acc, cur) => acc * cur, 1)
81
+ }
82
+
83
+ /**
84
+ * Returns the minimum value of an array of numbers
85
+ * @param array
86
+ * @category Array
87
+ * @example
88
+ * ```ts
89
+ * min([1, 2, 3, 4]) // 1
90
+ * min([1, 2, 3, 4, -1]) // -1
91
+ * ```
92
+ */
93
+ export function min(array: readonly number[]): number {
94
+ return Math.min(...array)
95
+ }
96
+
97
+ /**
98
+ * Returns the maximum value of an array of numbers
99
+ * @param array
100
+ * @category Array
101
+ * @example
102
+ * ```ts
103
+ * max([1, 2, 3, 4]) // 4
104
+ * max([1, 2, 3, 4, -1]) // 4
105
+ * ```
106
+ */
107
+ export function max(array: readonly number[]): number {
108
+ return Math.max(...array)
109
+ }
110
+
111
+ /**
112
+ * Returns the range of an array of numbers
113
+ * @param array
114
+ * @category Array
115
+ * @example
116
+ * ```ts
117
+ * range([1, 2, 3, 4]) // 3
118
+ * range([1, 2, 3, 4, -1]) // 5
119
+ * range([1, 2, 3, 4, -1, 10]) // 11
120
+ * ```
121
+ */
122
+ export function range(array: readonly number[]): number {
123
+ return max(array) - min(array)
124
+ }
125
+
126
+ /**
127
+ * Returns the variance of an array of numbers
128
+ * @param array
129
+ * @category Array
130
+ * @example
131
+ * ```ts
132
+ * variance([1, 2, 3, 4]) // 1.25
133
+ * ```
134
+ * @see https://en.wikipedia.org/wiki/Variance
135
+ */
136
+ export function variance(array: number[]): number {
137
+ const mean = average(array)
138
+ return average(array.map(num => (num - mean) ** 2))
139
+ }
140
+
141
+ /**
142
+ * Returns the standard deviation of an array of numbers
143
+ * @param array
144
+ * @category Array
145
+ * @example
146
+ * ```ts
147
+ * standardDeviation([1, 2, 3, 4]) // 1.118033988749895
148
+ * ```
149
+ * @see https://en.wikipedia.org/wiki/Standard_deviation
150
+ * @see https://stackoverflow.com/questions/7343890/standard-deviation-javascript
151
+ */
152
+ export function standardDeviation(array: number[]): number {
153
+ return Math.sqrt(variance(array))
154
+ }
155
+
156
+ /**
157
+ * Returns the z-score of a number in an array of numbers
158
+ * @param array
159
+ * @param num
160
+ * @category Array
161
+ * @example
162
+ * ```ts
163
+ * zScore([1, 2, 3, 4], 2) // 0
164
+ * zScore([1, 2, 3, 4], 3) // 0.7071067811865475
165
+ * ```
166
+ * @see https://en.wikipedia.org/wiki/Standard_score
167
+ */
168
+ export function zScore(array: number[], num: number): number {
169
+ return (num - average(array)) / standardDeviation(array)
170
+ }
171
+
172
+ /**
173
+ * Returns the percentile of a number in an array of numbers
174
+ * @param array
175
+ * @param num
176
+ * @category Array
177
+ * @example
178
+ * ```ts
179
+ * percentile([1, 2, 3, 4], 2) // 0.25
180
+ * percentile([1, 2, 3, 4], 3) // 0.75
181
+ * ```
182
+ * @see https://en.wikipedia.org/wiki/Percentile
183
+ */
184
+ export function percentile(array: number[], num: number): number {
185
+ return array.filter(n => n < num).length / array.length
186
+ }
187
+
188
+ /**
189
+ * Returns the interquartile range of an array of numbers
190
+ * @param array
191
+ * @category Array
192
+ * @example
193
+ * ```ts
194
+ * interquartileRange([1, 2, 3, 4]) // 1.5
195
+ * ```
196
+ * @see https://en.wikipedia.org/wiki/Interquartile_range
197
+ * @see https://stackoverflow.com/questions/48719873/how-to-calculate-interquartile-range-in-javascript
198
+ */
199
+ export function interquartileRange(array: number[]): number {
200
+ const q1 = median(array.slice(0, Math.floor(array.length / 2)))
201
+ const q3 = median(array.slice(Math.ceil(array.length / 2)))
202
+ return q3 - q1
203
+ }
204
+
205
+ /**
206
+ * Returns the covariance of two arrays of numbers
207
+ * @param array1
208
+ * @param array2
209
+ * @category Array
210
+ * @example
211
+ * ```ts
212
+ * covariance([1, 2, 3, 4], [1, 2, 3, 4]) // 1.25
213
+ * covariance([1, 2, 3, 4], [4, 3, 2, 1]) // -1.25
214
+ * ```
215
+ */
216
+ export function covariance(array1: number[], array2: number[]): number {
217
+ const mean1 = average(array1)
218
+ const mean2 = average(array2)
219
+ return average(array1.map((num1, i) => (num1 - mean1) * (array2[i] - mean2)))
220
+ }