pepka 0.12.2 → 0.13.0-b1

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/src/safe.ts CHANGED
@@ -1,404 +1,392 @@
1
- import { __, curry } from './curry'
2
- import { isNum, nul, isUndef, undef, isNull, isArray, isFunc, isStr, isObj } from './utils'
3
- import { qmergeDeep, qreduce, qappend, qmapKeys, qmergeDeepX, qmergeDeepAdd } from './quick'
4
- import { AnyFunc, Cond, AnyObject, Reducer } from './types'
5
- import { type } from './common'
6
- // over, lensProp
7
-
8
- export const equals = curry((a: any, b: any) => {
9
- const typea = type(a)
10
- if(typea===type(b) && (typea==='Object' || typea=='Array')) {
11
- if(isNull(a) || isNull(b)) {
12
- return a===b
13
- }
14
- if(a===b) {
15
- return true
16
- }
17
- for(const v of [a, b]) {
18
- for(const k in v) {
19
- if(
20
- !((v===b) && (k in a)) &&
21
- !((v===a) && (k in b) && equals(a[k], b[k]))
22
- ) {
23
- return false
24
- }
25
- }
26
- }
27
- return true
28
- }
29
- return a===b
30
- })
31
- export const ifElse = curry(
32
- (
33
- cond: (s: any) => boolean,
34
- pipeYes: (s: any) => any,
35
- pipeNo: (s: any) => any,
36
- s: any
37
- ) => cond(s) ? pipeYes(s) : pipeNo(s)
38
- )
39
- export const when = curry(
40
- (
41
- cond: (s: any) => boolean,
42
- pipe: (s: any) => any,
43
- s: any
44
- ) => ifElse(cond, pipe, identity, s)
45
- )
46
- export const compose = (
47
- (...fns: Function[]) =>
48
- (s: any = __) => {
49
- for(let i = length(fns)-1; i>-1; i--) {
50
- s = s===__ ? fns[i]() : fns[i](s)
51
- }
52
- return s
53
- }
54
- )// as F.Compose
55
-
56
- export const bind = curry(
57
- (fn: AnyFunc, context: any) => fn.bind(context)
58
- )
59
- export const nth = curry(
60
- (i: number, data: any[]) => data[i]
61
- )
62
- export const includes = curry(
63
- (s: any, ss: any[]) => {
64
- if(isStr(ss)) {
65
- return ss.includes(s)
66
- } else {
67
- for(const a of ss) {
68
- if(equals(a, s)) {
69
- return true
70
- }
71
- }
72
- return false
73
- }
74
- }
75
- )
76
- export const slice = curry(
77
- (from: number, to: number|null, o: any[] | string) =>
78
- o.slice(from, (isNum(to)?to:Infinity) as number)
79
- )
80
- export const head = nth(0)
81
- export const tail = slice(1, nul)
82
- export const add = curry((n: number, m: number) => n+m)
83
- export const subtract = curry((n: number, m: number) => m-n)
84
- export const flip = (fn: Function) => curry((b: any, a: any) => fn(a, b))
85
- export const isNil = (s: any) => isNull(s) || isUndef(s)
86
- export const length = (s: any[] | string) => s.length
87
- export const always = <T=any>(s: T) => () => s
88
- export const identity = (s: any) => s
89
- export const trim = (s: string) => s.trim()
90
- export const last = (s: any[] | string) => s[length(s)-1]
91
- export const not = (o: boolean) => !o
92
- export const complement = (fn: AnyFunc) => (...args: any) => {
93
- const out = fn(...args)
94
- return (isFunc(out) && (out as any).$args_left) ? complement(out) : not(out)
95
- }
96
- export const keys = (o: AnyObject | any[]) => Object.keys(o)
97
- export const values = (o: AnyObject | any[]) => Object.values(o)
98
- export const toPairs = (o: AnyObject | any[]) => Object.entries(o)
99
- export const test = curry((re: RegExp, s: string) => re.test(s))
100
- export const tap = curry((fn: Function, s: any) => { fn(s); return s })
101
- export const append = curry((s: any, xs: any[]) => [...xs, s])
102
- export const split = curry((s: string, xs: string) => xs.split(s))
103
- export const T = always<true>(true) as (...args: any[]) => true
104
- export const F = always<false>(false) as (...args: any[]) => false
105
- export const sizeof = (s: any[] | string | AnyObject) => {
106
- if(type(s) === 'Object') {
107
- let len = 0
108
- for(let _k in s as AnyObject) len++
109
- return len
110
- } else return length(s as any[])
111
- }
112
- export const range = curry((from: number, to: number) =>
113
- genBy(add(from), to-from)
114
- )
115
- export const uniq = (xs: any[]) => qreduce(
116
- (accum: any[], x: any) =>
117
- includes(x, accum) ? accum : qappend(x, accum),
118
- [], xs)
119
- export const intersection = curry(
120
- (xs1: any[], xs2: any[]) => xs1.filter(flip(includes)(xs2))
121
- )
122
- export const genBy = curry(
123
- (
124
- generator: (i: number) => any,
125
- length: number
126
- ) => [...Array(length)].map((_, i) => generator(i))
127
- )
128
- export const once = <Func extends AnyFunc>(fn: Func) => {
129
- let done = false, cache: any
130
- return (...args: Parameters<Func>) => {
131
- if(done) {
132
- return cache
133
- } else {
134
- done = true
135
- return cache = fn(...args)
136
- }
137
- }
138
- }
139
- export const reverse = (xs: any[]) => compose(
140
- (ln: number) => reduce(
141
- (nxs: any[], _: any, i: number) => qappend(xs[ln-i], nxs),
142
- [], xs
143
- ),
144
- add(-1),
145
- length
146
- )(xs)
147
- export const gt = curry(
148
- (a: number, b: number) => a>b
149
- )
150
- export const lt = curry(
151
- (a: number, b: number) => a<b
152
- )
153
- export const gte = curry(
154
- (a: number, b: number) => b>=a
155
- )
156
- export const lte = curry(
157
- (a: number, b: number) => b<=a
158
- )
159
- // : <U=any>(sortFn: (v: U)=>-1|1, xs: U[]) => U[]
160
- export const sort = curry((sortFn: any, xs: any[]) => xs.sort(sortFn))
161
- export const find = curry(
162
- (fn: Cond, s: any[]) => s.find(fn)
163
- )
164
- export const findIndex = curry(
165
- (fn: Cond, s: any[]) => s.findIndex(fn)
166
- )
167
- export const indexOf = curry(
168
- (x: any, xs: any[]) => findIndex(equals(x), xs)
169
- )
170
- export const explore = (caption: string, level = 'log') => tap(
171
- (v: any) => console[level](caption, v)
172
- )
173
- export const cond = curry(
174
- (pairs: [Cond, Function][], s: any) => {
175
- for(const [cond, fn] of pairs) {
176
- if(cond(s)) {
177
- return fn(s)
178
- }
179
- }
180
- }
181
- )
182
- export const assoc = curry(
183
- (prop: string, v: any, obj: AnyObject) => ({
184
- ...obj,
185
- [prop]: v
186
- })
187
- )
188
- export const assocPath = curry(
189
- (_path: string[], v: any, o: AnyObject) => compose(
190
- (first: string) => assoc(
191
- first,
192
- length(_path)<2
193
- ? v
194
- : assocPath(slice(1, null, _path), v, isObj(o[first]) ? o[first] : {}),
195
- o
196
- ),
197
- head
198
- )(_path)
199
- )
200
- export const all = curry((pred: Cond, xs: any[]) => xs.every(pred))
201
- export const any = curry((pred: Cond, xs: any[]) => xs.some(pred))
202
- export const allPass = curry(
203
- (preds: Cond[], x: any) => preds.every((pred) => pred(x))
204
- )
205
- export const anyPass = curry(
206
- (preds: Cond[], x: any) => preds.some((pred) => pred(x))
207
- )
208
- export const prop = curry(
209
- (key: string, o: AnyObject) => o[key]
210
- )
211
- export const propEq = curry(
212
- (key: string, value: any, o: AnyObject) => equals(o[key], value)
213
- )
214
- export const propsEq = curry(
215
- (key: string, o1: any, o2: AnyObject) => equals(o1[key], o2[key])
216
- )
217
- export const pathOr = curry(
218
- (_default: any, path: string[], o: any) =>
219
- ifElse(length,
220
- () => isNil(o)
221
- ? _default
222
- : compose(
223
- ifElse(isNil,
224
- always(_default),
225
- (o: any) => pathOr(_default, slice(1, nul, path), o)
226
- ),
227
- flip(prop)(o),
228
- head
229
- )(path),
230
- always(o),
231
- path)
232
- )
233
- export const path = pathOr(undef)
234
- export const pathEq = curry(
235
- (_path: string[], value: any, o: AnyObject) => equals(path(_path, o), value)
236
- )
237
- export const pathsEq = curry(
238
- (_path: string[], o1: AnyObject, o2: AnyObject) =>
239
- equals(path(_path, o1), path(_path, o2))
240
- )
241
- const typed_arr_re = /^(.*?)(8|16|32|64)(Clamped)?Array$/
242
- export const clone = (s: any) => {
243
- const t = type(s)
244
- switch(t) {
245
- case 'Null': return s
246
- case 'Array': return map(clone, s)
247
- case 'Object':
248
- const out = {}
249
- for(let k in s) {
250
- out[k] = clone(s[k])
251
- }
252
- return out
253
- case 'String': case 'Number':
254
- case 'Boolean': case 'Symbol':
255
- return s
256
- default:
257
- return typed_arr_re.test(t) ? map(clone, s) : s
258
- }
259
- }
260
- export const reduce = curry(
261
- (fn: Reducer, accum: any, arr: any[]) =>
262
- qreduce(fn, clone(accum), arr)
263
- )
264
- export const pickBy = curry(
265
- (cond: Cond, o: AnyObject) => filter(cond, o)
266
- )
267
- export const pick = curry(
268
- (props: string[], o: AnyObject) => {
269
- const out = {}
270
- for(const p of props) {
271
- if(p in o) {
272
- out[p] = o[p]
273
- }
274
- }
275
- return out
276
- }
277
- )
278
- export const omit = curry(
279
- (props: string[], o: AnyObject) => filter(
280
- (_: any, k: string) => !includes(k, props),
281
- o
282
- )
283
- )
284
- export const fromPairs = (pairs: [string, any][]) => reduce(
285
- (o: AnyObject, pair: [string, any]) => assoc(...pair, o),
286
- {}, pairs
287
- )
288
- type Concat = ((a: string, b: string) => string)
289
- | ((a: any[], b: any[]) => any[])
290
- export const concat = curry(
291
- ((a, b) => a.concat(b)) as Concat
292
- )
293
- export const join = curry(
294
- (delimeter: string, arr: string[]) => arr.join(delimeter)
295
- )
296
- export const map = curry(
297
- (pipe: (s: any) => any, arr: any[]) => arr.map(pipe)
298
- )
299
- export const forEach = curry(
300
- (pipe: (s: any) => any, arr: any[]) => arr.forEach(pipe)
301
- )
302
- export const both = curry(
303
- (cond1: Cond, cond2: Cond, s: any) => cond2(s) && cond1(s)
304
- )
305
- export const isEmpty = (s: any) => {
306
- switch(type(s)) {
307
- case 'String': case 'Array': return length(s)==0
308
- case 'Object':
309
- for(const _k in s) return false
310
- return true
311
- default: return null
312
- }
313
- }
314
- export const empty = (s: any) => {
315
- switch(type(s)) {
316
- case 'String': return ''
317
- case 'Object': return {}
318
- case 'Array': return []
319
- default: return undef
320
- }
321
- }
322
- export const replace = curry(
323
- (
324
- a: string | RegExp,
325
- b: string,
326
- where: string
327
- ) => where.replace(a, b)
328
- )
329
- export const filter = curry(
330
- (
331
- cond: (v: any, k: string | number) => boolean,
332
- data: any[] | AnyObject
333
- ) => isArray(data)
334
- ? data.filter(cond)
335
- : compose(
336
- fromPairs,
337
- filter(([k, v]) => cond(v, k)),
338
- toPairs
339
- )(data)
340
- )
341
- export const memoize = (fn: Function) => {
342
- let cache: any
343
- let cached = false
344
- return () => cached ? cache : (cached = true, cache = fn())
345
- }
346
- export const mergeShallow = curry(
347
- (o1: AnyObject, o2: AnyObject): AnyObject =>
348
- Object.assign({}, o1, o2)
349
- )
350
- export const mergeDeep = curry(
351
- (a: AnyObject, b: AnyObject) => qmergeDeep(clone(a), clone(b))
352
- )
353
- export const mergeDeepX = curry(
354
- (a: AnyObject, b: AnyObject) => qmergeDeepX(clone(a), clone(b))
355
- )
356
- export const mergeDeepAdd = curry(
357
- (a: AnyObject, b: AnyObject) => qmergeDeepAdd(clone(a), clone(b))
358
- )
359
- export const overProp = curry(
360
- (prop: string, pipe: AnyFunc, data: any) =>
361
- assoc(prop, pipe(data[prop]), data)
362
- )
363
- /** mapKeys({ a: 'b' }, { a: 44 }) -> { b: 44 } */
364
- export const mapKeys = curry(
365
- (
366
- keyMap: {[oldKey: string]: string},
367
- o: AnyObject
368
- ) => qmapKeys(keyMap, Object.assign({}, o))
369
- )
370
-
371
- // ASYNCS
372
-
373
- /** One promise waits for another. */
374
- export const forEachSerial = (() => {
375
- const pipe = async (fn: AnyFunc, items: any[], i: number) => {
376
- if(i<items.length) {
377
- await fn(items[i])
378
- await pipe(fn, items, ++i)
379
- }
380
- }
381
- return curry(
382
- (fn: AnyFunc, items: any[]) => pipe(fn, items, 0)
383
- )
384
- })()
385
- /** Promise.all wrapper for functional pipelining. */
386
- export const waitAll = (promises: Promise<any>[]) => Promise.all(promises)
387
- /** Waits for all promises mapped by the fn. */
388
- export const forEachAsync = curry(
389
- (fn: (item: any) => Promise<any>, items: any[]) =>
390
- Promise.all(items.map(fn))
391
- )
392
- /** The same as compose, but waits for promises in chains and returns a Promise. */
393
- export const composeAsync = (() => {
394
- const pipe = async (fns: AnyFunc[], data: any, i: number): Promise<any> =>
395
- ~i ? await pipe(fns, await fns[i](data), --i) : data
396
- return <T = any>(...fns: AnyFunc[]) =>
397
- (data?: any) => pipe(fns, data, fns.length-1) as Promise<T>
398
- })()
399
-
400
- // ALIASES
401
-
402
- export const mirror = identity
403
- export const reflect = identity
404
- export const echo = identity
1
+ import { __, curry, curry2, curry3 } from './curry'
2
+ import { isNum, nul, isUndef, undef, isNull, isArray, isFunc, isStr, isObj } from './utils'
3
+ import { qmergeDeep, qreduce, qappend, qmapKeys, qmergeDeepX, qmergeDeepAdd } from './quick'
4
+ import { AnyFunc, Cond, AnyObject, Reducer } from './types'
5
+ import { type } from './common'
6
+ import { F as FT } from 'ts-toolbelt'
7
+ // over, lensProp
8
+
9
+ export const equals = curry2((a: any, b: any) => {
10
+ const typea = type(a)
11
+ if(typea===type(b) && (typea==='Object' || typea=='Array')) {
12
+ if(isNull(a) || isNull(b)) {
13
+ return a===b
14
+ }
15
+ if(a===b) {
16
+ return true
17
+ }
18
+ for(const v of [a, b]) {
19
+ for(const k in v) {
20
+ if(
21
+ !((v===b) && (k in a)) &&
22
+ !((v===a) && (k in b) && equals(a[k], b[k]))
23
+ ) {
24
+ return false
25
+ }
26
+ }
27
+ }
28
+ return true
29
+ }
30
+ return a===b
31
+ })
32
+ export const ifElse = curry(
33
+ (
34
+ cond: (s: any) => boolean,
35
+ pipeYes: (s: any) => any,
36
+ pipeNo: (s: any) => any,
37
+ s: any
38
+ ) => cond(s) ? pipeYes(s) : pipeNo(s)
39
+ )
40
+ export const when = curry3(
41
+ (
42
+ cond: (s: any) => boolean,
43
+ pipe: (s: any) => any,
44
+ s: any
45
+ ) => ifElse(cond, pipe, identity, s)
46
+ )
47
+ export const compose: FT.Compose = (
48
+ (...fns: AnyFunc[]) =>
49
+ (s: any = __) => {
50
+ for(let i = length(fns)-1; i>-1; i--) {
51
+ s = s===__ ? fns[i]() : fns[i](s)
52
+ }
53
+ return s
54
+ }
55
+ )
56
+
57
+ export const bind = curry2<AnyFunc>(
58
+ (fn: AnyFunc, context: any) => fn.bind(context)
59
+ )
60
+
61
+ const _nth = <T=any>(i: number, data: T[]) => data[i]
62
+ export const nth = curry2(_nth)
63
+
64
+ export const includes = curry2(
65
+ (s: any, ss: any[]) => {
66
+ if(isStr(ss)) {
67
+ return ss.includes(s)
68
+ } else {
69
+ for(const a of ss) {
70
+ if(equals(a, s)) {
71
+ return true
72
+ }
73
+ }
74
+ return false
75
+ }
76
+ }
77
+ )
78
+ export const slice = curry3(
79
+ (from: number, to: number|null, o: any[] | string) =>
80
+ o.slice(from, (isNum(to)?to:Infinity) as number)
81
+ )
82
+ export const head = nth(0)
83
+ export const tail = slice(1, nul)
84
+ export const add = curry2((n: number, m: number) => n+m)
85
+ export const subtract = curry2((n: number, m: number) => m-n)
86
+ export const flip = (fn: Function) => curry((b: any, a: any) => fn(a, b))
87
+ export const isNil = (s: any) => isNull(s) || isUndef(s)
88
+ export const length = (s: any[] | string) => s.length
89
+ export const always = <T=any>(s: T) => () => s
90
+ export const identity = (s: any) => s
91
+ export const trim = (s: string) => s.trim()
92
+ export const last = (s: any[] | string) => s[length(s)-1]
93
+ export const not = (o: boolean) => !o
94
+ export const complement = (fn: AnyFunc) => (...args: any) => {
95
+ const out = fn(...args)
96
+ return (isFunc(out) && (out as any).$args_left) ? complement(out) : not(out)
97
+ }
98
+ export const keys = (o: AnyObject | any[]) => Object.keys(o)
99
+ export const values = (o: AnyObject | any[]) => Object.values(o)
100
+ export const toPairs = (o: AnyObject | any[]) => Object.entries(o)
101
+ export const test = curry2((re: RegExp, s: string) => re.test(s))
102
+ export const tap = curry2((fn: Function, s: any) => { fn(s); return s })
103
+ export const append = curry2((s: any, xs: any[]) => [...xs, s])
104
+ export const split = curry2((s: string, xs: string) => xs.split(s))
105
+ export const T = always<true>(true) as (...args: any[]) => true
106
+ export const F = always<false>(false) as (...args: any[]) => false
107
+ export const sizeof = (s: any[] | string | AnyObject) => {
108
+ if(type(s) === 'Object') {
109
+ let len = 0
110
+ for(let _k in s as AnyObject) len++
111
+ return len
112
+ } else return length(s as any[])
113
+ }
114
+ export const range = curry2((from: number, to: number) =>
115
+ genBy(add(from), to-from)
116
+ )
117
+ export const uniq = (xs: any[]) => qreduce(
118
+ (accum: any[], x: any) =>
119
+ includes(x, accum) ? accum : qappend(x, accum),
120
+ [], xs)
121
+ export const intersection = curry2(
122
+ (xs1: any[], xs2: any[]) => xs1.filter(flip(includes)(xs2))
123
+ )
124
+ export const genBy = curry2(
125
+ (
126
+ generator: (i: number) => any,
127
+ length: number
128
+ ) => [...Array(length)].map((_, i) => generator(i))
129
+ )
130
+ export const once = <Func extends AnyFunc>(fn: Func) => {
131
+ let done = false, cache: any
132
+ return (...args: Parameters<Func>) => {
133
+ if(done) {
134
+ return cache
135
+ } else {
136
+ done = true
137
+ return cache = fn(...args)
138
+ }
139
+ }
140
+ }
141
+ export const reverse = (xs: any[]) => compose(
142
+ (ln: number) => reduce(
143
+ (nxs: any[], _: any, i: number) => qappend(xs[ln-i], nxs),
144
+ [], xs
145
+ ),
146
+ add(-1),
147
+ length
148
+ )(xs)
149
+ export const gt = curry2( (a: number, b: number) => a>b )
150
+ export const lt = curry2( (a: number, b: number) => a<b )
151
+ export const gte = curry2( (a: number, b: number) => b>=a )
152
+ export const lte = curry2( (a: number, b: number) => b<=a )
153
+ export const sort = curry2((sortFn: any, xs: any[]) => xs.sort(sortFn))
154
+ export const find = curry2((fn: Cond, s: any[]) => s.find(fn))
155
+ export const findIndex = curry2((fn: Cond, s: any[]) => s.findIndex(fn))
156
+ export const indexOf = curry2((x: any, xs: any[]) => findIndex(equals(x), xs))
157
+ export const explore = (caption: string, level = 'log') => tap(
158
+ (v: any) => console[level](caption, v)
159
+ )
160
+ export const cond = curry2(
161
+ (pairs: [Cond, Function][], s: any) => {
162
+ for(const [cond, fn] of pairs) {
163
+ if(cond(s)) {
164
+ return fn(s)
165
+ }
166
+ }
167
+ }
168
+ )
169
+ export const assoc = curry3(
170
+ (prop: string, v: any, obj: AnyObject) => ({
171
+ ...obj,
172
+ [prop]: v
173
+ })
174
+ )
175
+ export const assocPath = curry3(
176
+ (_path: string[], v: any, o: AnyObject) => compose(
177
+ (first: string) => assoc(
178
+ first,
179
+ length(_path)<2
180
+ ? v
181
+ : assocPath(slice(1, null, _path), v, isObj(o[first]) ? o[first] : {}),
182
+ o
183
+ ),
184
+ head
185
+ )(_path)
186
+ )
187
+ export const all = curry2((pred: Cond, xs: any[]) => xs.every(pred))
188
+ export const any = curry2((pred: Cond, xs: any[]) => xs.some(pred))
189
+ export const allPass = curry2(
190
+ (preds: Cond[], x: any) => preds.every((pred) => pred(x))
191
+ )
192
+ export const anyPass = curry2(
193
+ (preds: Cond[], x: any) => preds.some((pred) => pred(x))
194
+ )
195
+ export const prop = curry2( (key: string, o: AnyObject) => o[key] )
196
+ export const propEq = curry3(
197
+ (key: string, value: any, o: AnyObject) => equals(o[key], value)
198
+ )
199
+ export const propsEq = curry3(
200
+ (key: string, o1: any, o2: AnyObject) => equals(o1[key], o2[key])
201
+ )
202
+ export const pathOr = curry3(
203
+ (_default: any, path: string[], o: any) =>
204
+ ifElse(length,
205
+ () => isNil(o)
206
+ ? _default
207
+ : compose(
208
+ ifElse(isNil,
209
+ always(_default),
210
+ (o: any) => pathOr(_default, slice(1, nul, path), o)
211
+ ),
212
+ flip(prop)(o),
213
+ head
214
+ )(path),
215
+ always(o),
216
+ path)
217
+ )
218
+ export const path = pathOr(undef)
219
+ export const pathEq = curry3(
220
+ (_path: string[], value: any, o: AnyObject) => equals(path(_path, o), value)
221
+ )
222
+ export const pathsEq = curry3(
223
+ (_path: string[], o1: AnyObject, o2: AnyObject) =>
224
+ equals(path(_path, o1), path(_path, o2))
225
+ )
226
+ const typed_arr_re = /^(.*?)(8|16|32|64)(Clamped)?Array$/
227
+ export const clone = (s: any, shallow = false) => {
228
+ const t = type(s)
229
+ switch(t) {
230
+ case 'Null': return s
231
+ case 'Array': return shallow ? [...s] : map(clone, s)
232
+ case 'Object':
233
+ if(shallow) return {...s}
234
+ const out = {}
235
+ for(let k in s) {
236
+ out[k] = clone(s[k])
237
+ }
238
+ return out
239
+ case 'String': case 'Number':
240
+ case 'Boolean': case 'Symbol':
241
+ return s
242
+ default:
243
+ return typed_arr_re.test(t) ? s.constructor.from(s) : s
244
+ }
245
+ }
246
+ export const cloneShallow = (s: any) => clone(s, true)
247
+
248
+ export const reduce = curry3(
249
+ (fn: Reducer, accum: any, arr: any[]) =>
250
+ qreduce(fn, clone(accum), arr)
251
+ )
252
+ export const pickBy = curry2(
253
+ (cond: Cond, o: AnyObject) => filter(cond, o)
254
+ )
255
+ export const pick = curry2(
256
+ (props: string[], o: AnyObject) => {
257
+ const out = {}
258
+ for(const p of props) {
259
+ if(p in o) {
260
+ out[p] = o[p]
261
+ }
262
+ }
263
+ return out
264
+ }
265
+ )
266
+ export const omit = curry2(
267
+ (props: string[], o: AnyObject) => filter(
268
+ (_: any, k: string) => !includes(k, props),
269
+ o
270
+ )
271
+ )
272
+ export const fromPairs = (pairs: [string, any][]) => reduce(
273
+ (o: AnyObject, pair: [string, any]) => assoc(...pair, o),
274
+ {}, pairs
275
+ )
276
+ type Concat = ((a: string, b: string) => string)
277
+ | ((a: any[], b: any[]) => any[])
278
+ export const concat = curry2(
279
+ ((a, b) => a.concat(b)) as Concat
280
+ )
281
+ export const join = curry2(
282
+ (delimeter: string, arr: string[]) => arr.join(delimeter)
283
+ )
284
+ export const map = curry2(
285
+ (pipe: (s: any) => any, arr: any[]) => arr.map(pipe)
286
+ )
287
+ export const forEach = curry2(
288
+ (pipe: (s: any) => any, arr: any[]) => arr.forEach(pipe)
289
+ )
290
+ export const both = curry3(
291
+ (cond1: Cond, cond2: Cond, s: any) => cond2(s) && cond1(s)
292
+ )
293
+ export const isEmpty = (s: any) => {
294
+ switch(type(s)) {
295
+ case 'String': case 'Array': return length(s)==0
296
+ case 'Object':
297
+ for(const _k in s) return false
298
+ return true
299
+ default: return null
300
+ }
301
+ }
302
+ export const empty = (s: any) => {
303
+ switch(type(s)) {
304
+ case 'String': return ''
305
+ case 'Object': return {}
306
+ case 'Array': return []
307
+ default: return undef
308
+ }
309
+ }
310
+ export const replace = curry3(
311
+ (
312
+ a: string | RegExp,
313
+ b: string,
314
+ where: string
315
+ ) => where.replace(a, b)
316
+ )
317
+ export const filter = curry2(
318
+ (
319
+ cond: (v: any, k: string | number) => boolean,
320
+ data: any[] | AnyObject
321
+ ) => isArray(data)
322
+ ? data.filter(cond)
323
+ : compose(
324
+ fromPairs,
325
+ filter(([k, v]) => cond(v, k)),
326
+ toPairs
327
+ )(data)
328
+ )
329
+ export const memoize = (fn: Function) => {
330
+ let cache: any
331
+ let cached = false
332
+ return () => cached ? cache : (cached = true, cache = fn())
333
+ }
334
+ export const mergeShallow = curry2(
335
+ (o1: AnyObject, o2: AnyObject): AnyObject =>
336
+ Object.assign({}, o1, o2)
337
+ )
338
+ export const mergeDeep = curry2(
339
+ (a: AnyObject, b: AnyObject) => qmergeDeep(clone(a), clone(b))
340
+ )
341
+ export const mergeDeepX = curry2(
342
+ (a: AnyObject, b: AnyObject) => qmergeDeepX(clone(a), clone(b))
343
+ )
344
+ export const mergeDeepAdd = curry2(
345
+ (a: AnyObject, b: AnyObject) => qmergeDeepAdd(clone(a), clone(b))
346
+ )
347
+ export const overProp = curry3(
348
+ (prop: string, pipe: AnyFunc, data: any) =>
349
+ assoc(prop, pipe(data[prop]), data)
350
+ )
351
+ /** mapKeys({ a: 'b' }, { a: 44 }) -> { b: 44 } */
352
+ export const mapKeys = curry2(
353
+ (
354
+ keyMap: {[oldKey: string]: string},
355
+ o: AnyObject
356
+ ) => qmapKeys(keyMap, Object.assign({}, o))
357
+ )
358
+
359
+ // ASYNCS
360
+
361
+ /** One promise waits for another. */
362
+ export const forEachSerial = (() => {
363
+ const pipe = async (fn: AnyFunc, items: any[], i: number) => {
364
+ if(i<items.length) {
365
+ await fn(items[i])
366
+ await pipe(fn, items, ++i)
367
+ }
368
+ }
369
+ return curry2(
370
+ (fn: AnyFunc, items: any[]) => pipe(fn, items, 0)
371
+ )
372
+ })()
373
+ /** Promise.all wrapper for functional pipelining. */
374
+ export const waitAll = (promises: Promise<any>[]) => Promise.all(promises)
375
+ export const waitTap = curry2(async (fn: Function, s: any) => { await fn(s); return s })
376
+ /** Waits for all promises mapped by the fn. */
377
+ export const forEachAsync = curry2(
378
+ (fn: (item: any) => Promise<any>, items: any[]) =>
379
+ Promise.all(items.map(fn))
380
+ )
381
+ /** The same as compose, but waits for promises in chains and returns a Promise. */
382
+ export const composeAsync = (() => {
383
+ const pipe = async (fns: AnyFunc[], data: any, i: number): Promise<any> =>
384
+ ~i ? await pipe(fns, await fns[i](data), --i) : data
385
+ return <T = any>(...fns: AnyFunc[]) =>
386
+ (data?: any) => pipe(fns, data, fns.length-1) as Promise<T>
387
+ })() as FT.Compose<'async'>
388
+
389
+ // ALIASES
390
+ export const mirror = identity
391
+ export const reflect = identity
392
+ export const echo = identity