-
Notifications
You must be signed in to change notification settings - Fork 0
/
object.ts
250 lines (223 loc) · 6.44 KB
/
object.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
import type { Unary, Binary, Ternary } from './data.ts'
export type Entries<T> = Array<{
[K in keyof T]: [K, T[K]]
}[keyof T]>
/** return the entries of an object */
export function entries<T extends Record<string, any>>(o: T): Entries<T> {
return Object.entries(o)
}
/** generate an object from its entries */
export function fromEntries<T extends Record<any, any>>(xs: Entries<T>): T {
return Object.fromEntries(xs) as T;
}
/** return an array of an object's values */
export function values<T extends object>(xs: T): Array<T[keyof T]> {
return Object.values(xs)
}
/** map implementation for objects
*
* given a mapping function **A** → **B** and an object of **As**,
* return a new object of **Bs**
*/
export function map<A, B>(f: Unary<A, B>) {
return function<K extends string>(xs: Record<K, A>): Record<K, B> {
const o: Record<string, B> = {}
for (const [k, v] of entries(xs))
o[k] = f(v)
return o as Record<K, B>
}
}
export function map2<K1 extends string, K2 extends string>(f: Unary<K1, K2>) {
return function<V1, V2>(g: Unary<V1, V2>) {
return function (xs: Record<K1, V1>): Record<K2, V2> {
const o: Partial<Record<K2, V2>> = {}
for (const [k, v] of entries(xs))
o[f(k)] = g(v)
return o as any
}
}
}
export function mapKeys<A extends string, B extends string>(f: Unary<A, B>) {
return function<V>(xs: Record<A, V>): Record<B, V> {
const o: Record<string, V> = {}
for (const [k, v] of entries(xs))
o[f(k)] = v
return o
}
}
export function eachWithKeys<K extends string, V>(f: Binary<K, V, any>): (xs: Record<K, V>) => void {
return function (xs) {
for (const [k, v] of entries(xs))
f(k)(v)
}
}
/** filter implementation for objects
*
* given a testing function **A** → *boolean* and an object of **As**
* return a new object of only the **As** for which the testing function
* returns *true*
*/
export function filter<A, K extends string>(f: Unary<A, boolean>): (x: Record<K, A>) => Partial<Record<K, A>> {
return function (xs) {
const o: Partial<Record<K, A>> = {}
for (const [k, v] of entries(xs))
if (f(v))
o[k] = v
return o
}
}
/** filter implentation for objects, based on keys as well as values
*
* given a testing function **[K, A]** → *boolean* and an object of **As** with keys **Ks**
* return a new object of only the **As** for which the testing function
* returns *true*
*
* since the keys are also passed, it is possible to filter based on keys
*/
export function filterWithKeys<A, K extends string>(f: Binary<K, A, boolean>): (x: Record<K, A>) => Partial<Record<K, A>> {
return function (xs) {
const o: Partial<Record<K, A>> = {}
for (const x of entries(xs))
if (f(x[0])(x[1]))
o[x[0]] = x[1]
return o
}
}
/** left fold for objects
*
* successively apply a binary function **A** → **B** → **A**
* to a collection of **Bs**, accumulating the result into **A**
*
* finally, return the accumulated value
*
* the initial value is given by **i**
*/
export function foldl<A, B, K extends string>(f: Binary<B, A, B>, i: B): (xs: Record<K, A>) => B {
return function(xs) {
let a = i
for (const x of values(xs))
a = f(a)(x)
return a
}
}
export function foldlWithKeys<A, B, K extends string>(f: Ternary<B, K, A, B>, i: B): (xs: Record<K, A>) => B {
return function (xs) {
let a = i
for (const [k, v] of entries(xs))
a = f(a)(k)(v)
return a
}
}
/** right fold for objects
*
* successively apply a binary function **B** → **A** → **A**
* to a collection of **Bs**, accumulating the result into **A**
*
* finally, return the accumulated value
*
* the initial value is given by **i**
*/
export function foldr<A, B, K extends string>(f: Binary<A, B, B>, i: B): (xs: Record<K, A>) => B {
return function(xs) {
let a = i
for (const x of values(xs))
a = f(x)(a)
return a
}
}
export function foldrWithKeys<A, B, K extends string>(f: Ternary<K, A, B, B>, i: B): (xs: Record<K, A>) => B {
return function (xs) {
let a = i
for (const [k, v] of entries(xs))
a = f(k)(v)(a)
return a
}
}
/**
* Set defaults on an object IN PLACE
* returns the mutated object
*/
export function defaults<T extends object>(x: Partial<T>): (d: T) => T {
return function(d) {
for (const [k, v] of entries(d))
if (!x.hasOwnProperty(k))
x[k] = v
return x as T;
}
}
export function update_with<A extends object>(source: A) {
return function <B extends A>(dest: B): B {
for (const [k, v] of entries(source))
// @ts-ignore
dest[k] = v;
return dest;
}
}
export function into<R extends Record<string|number|symbol, any>>(o: R): <K extends keyof R>(k: K) => (x: R[K]) => typeof o {
return function (k) {
return function (x) {
o[k] = x
return o
}
}
}
/** filter out keys of an object whose values are undefined */
export function defined<T extends Record<any, any>>(x: T): Partial<T> {
const y: Partial<T> = {};
for (const [k, v] of entries(x))
if (v !== undefined)
y[k] = v
return y;
}
export function get<R extends Record<string|symbol, any>, K extends keyof R>(k: K): (x: R) => R[K] {
return function (x) {
return x[k]
}
}
/** merge two objects into one object */
export function merge<T extends Record<string, any>>(a: Partial<T>): (b: T) => T {
return function (b) {
return { ...a, ...b }
}
}
/** returns the number of entries any record has */
export function len(x: Record<any, any>): number {
return Object.keys(x).length
}
export function ofK<K extends string, V>(f: Unary<K, V>): (xs: Iterable<K>) => Record<K, V> {
return function (xs) {
const o: Partial<Record<K, V>> = {}
for (const x of xs)
o[x] = f(x)
return o as any
}
}
export function ofV<K extends string, V>(f: Unary<V, K>): (xs: Iterable<V>) => Record<K, V> {
return function (xs) {
const o: Partial<Record<K, V>> = {}
for (const x of xs)
o[f(x)] = x
return o as any
}
}
export function ofKV<T, K extends string>(f: Unary<T, K>) {
return function<V>(g: Unary<T, V>) {
return function(xs: Iterable<T>): Record<K, V> {
const o: Partial<Record<K, V>> = {}
for (const x of xs)
o[f(x)] = g(x)
return o as any
}
}
}
export const assoc =
<K extends string>(k: K) =>
<V>(v: V) =>
<O extends Record<any, any>>(o: O): O & Record<K, V> =>
({ ...o, [k]: v })
export async function awaited<T extends Record<any, Promise<any>>>(xs: T): Promise<{[K in keyof T]: Awaited<T[K]>}> {
const o: Partial<{[K in keyof T]: Awaited<T[K]>}> = {}
for (const [k, v] of entries(xs))
o[k] = await v
return o as any
}