pepka 1.6.23 → 1.7.0

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/dist/bundle.cjs CHANGED
@@ -136,9 +136,9 @@ const includes = curry2((s, ss) => {
136
136
  }
137
137
  });
138
138
 
139
- /* Then next fns seem to be excess due to their safe ver performance should be the same or better:
140
- * qflat, qpick, qslice, quniq, qflat, qflatShallow, qreduceAsync
141
- */
139
+ const { min } = Math;
140
+ const z = 0;
141
+ /* qflat, qflatShallow, qreduceAsync */
142
142
  const qappend = curry2((s, xs) => { xs.push(s); return xs; });
143
143
  const qassoc = curry3((prop, v, obj) => { obj[prop] = v; return obj; });
144
144
  const qreduce = curry3((fn, accum, arr) => arr.reduce(fn, accum));
@@ -254,6 +254,55 @@ const qomit = curry2((props, o) => qfilter((_, k) => !includes(k, props), o));
254
254
  /** @param prop string @param pipe (data[prop]): prop_value @param data any
255
255
  * @returns data with prop over pipe. */
256
256
  const qoverProp = curry3((prop, pipe, data) => qassoc(prop, pipe(data[prop]), data));
257
+ /** Slower than pick() (dictionary mode) !
258
+ * @param props (string|number)[]
259
+ * @param o AnyObject
260
+ * @returns AnyObject
261
+ */
262
+ const qpick = curry2((props, o) => {
263
+ for (const p in o)
264
+ if (!props.includes(p))
265
+ delete o[p];
266
+ return o;
267
+ });
268
+ const qslice = curry3((from, to, xs) => {
269
+ const right = (isNum(to) ? to : inf);
270
+ const window_width = min(right, length(xs)) - from;
271
+ if (isArray(xs)) {
272
+ xs = xs;
273
+ if (from > z)
274
+ for (let i = z; i < window_width; i++)
275
+ xs[i] = xs[from + i];
276
+ xs.length = window_width;
277
+ return xs;
278
+ }
279
+ else
280
+ return xs.slice(from, right); // strings are immutable.
281
+ });
282
+ /** Should be faster than .splice() 'cause does not make a new array. */
283
+ const rmel = (index, xs) => {
284
+ const len = length(xs);
285
+ for (let i = index; i < len; i++)
286
+ xs[i] = xs[i + 1];
287
+ xs.length = len - 1;
288
+ return xs;
289
+ };
290
+ const seen = new Set();
291
+ const quniq = (xs) => {
292
+ seen.clear();
293
+ let size = length(xs);
294
+ for (let i = z; i < size; i++) {
295
+ const x = xs[i];
296
+ if (seen.has(x)) {
297
+ rmel(i, xs);
298
+ size--;
299
+ i--;
300
+ }
301
+ else
302
+ seen.add(x);
303
+ }
304
+ return xs;
305
+ };
257
306
  // Aliases.
258
307
  const qpush = qappend;
259
308
 
@@ -380,7 +429,12 @@ const diff = curry2((_xs1, _xs2) => {
380
429
  }
381
430
  return out;
382
431
  });
383
- const genBy = curry2((generator, length) => [...Array(length)].map((_, i) => generator(i)));
432
+ const genBy = curry2((generator, length) => {
433
+ const a = new Array(length);
434
+ for (let i = 0; i < length; i++)
435
+ a[i] = generator(i);
436
+ return a;
437
+ });
384
438
  const once = (fn) => {
385
439
  let done = false, cache;
386
440
  return function (...args) {
@@ -471,6 +525,11 @@ const freezeShallow = (o) => qfreezeShallow(clone(o));
471
525
  * @param array T2[]
472
526
  */
473
527
  const reduce = curry3((reducer, accum, arr) => qreduce(reducer, clone(accum), arr));
528
+ /**
529
+ * @param props (string|number)[]
530
+ * @param o AnyObject
531
+ * @returns AnyObject
532
+ */
474
533
  const pick = curry2((props, o) => {
475
534
  const out = {};
476
535
  for (const p of props)
@@ -554,6 +613,7 @@ const notf = complement;
554
613
  const push = append;
555
614
  const some = any;
556
615
  const weakEq = eq;
616
+ const uniqBy = uniqWith;
557
617
 
558
618
  /** One promise waits for another. */
559
619
  const forEachSerial = (() => {
@@ -775,11 +835,14 @@ exports.qmergeDeepX = qmergeDeepX;
775
835
  exports.qmergeShallow = qmergeShallow;
776
836
  exports.qomit = qomit;
777
837
  exports.qoverProp = qoverProp;
838
+ exports.qpick = qpick;
778
839
  exports.qprepend = qprepend;
779
840
  exports.qpush = qpush;
780
841
  exports.qreduce = qreduce;
781
842
  exports.qreverse = qreverse;
843
+ exports.qslice = qslice;
782
844
  exports.qsort = qsort;
845
+ exports.quniq = quniq;
783
846
  exports.range = range;
784
847
  exports.reduce = reduce;
785
848
  exports.reflect = reflect;
@@ -807,6 +870,7 @@ exports.type = type;
807
870
  exports.typeIs = typeIs;
808
871
  exports.uncurry = uncurry;
809
872
  exports.uniq = uniq;
873
+ exports.uniqBy = uniqBy;
810
874
  exports.uniqWith = uniqWith;
811
875
  exports.values = values;
812
876
  exports.wait = wait;
package/dist/bundle.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
3
  type AnyArgs = any[];
4
- type AnyArray<T = any> = T[] | readonly T[];
4
+ type AnyArray<T = any> = T[] | readonly T[] | (ArrayBufferView & {
5
+ length: number;
6
+ });
5
7
  type Split<S extends string> = S extends `${infer U}${infer V}` ? [
6
8
  U,
7
9
  ...Split<V>
@@ -190,6 +192,19 @@ export declare const qomit: {
190
192
  /** @param prop string @param pipe (data[prop]): prop_value @param data any
191
193
  * @returns data with prop over pipe. */
192
194
  export declare const qoverProp: (...args: AnyArgs) => any;
195
+ /** Slower than pick() (dictionary mode) !
196
+ * @param props (string|number)[]
197
+ * @param o AnyObject
198
+ * @returns AnyObject
199
+ */
200
+ export declare const qpick: {
201
+ (a: Placeholder, b: AnyObject): (a: string[]) => AnyObject;
202
+ (a: string[], b: Placeholder): (b: AnyObject) => AnyObject;
203
+ (a: string[]): (b: AnyObject) => AnyObject;
204
+ (a: string[], b: AnyObject): AnyObject;
205
+ };
206
+ export declare const qslice: (...args: AnyArgs) => any;
207
+ export declare const quniq: (xs: any[]) => any[];
193
208
  export declare const qpush: {
194
209
  (a: Placeholder, b: any[]): (a: any) => any[];
195
210
  (a: any, b: Placeholder): (b: any[]) => any[];
@@ -543,11 +558,16 @@ export declare const freezeShallow: <T extends AnyObject>(o: T) => Readonly<T>;
543
558
  * @param array T2[]
544
559
  */
545
560
  export declare const reduce: (...args: AnyArgs) => any;
561
+ /**
562
+ * @param props (string|number)[]
563
+ * @param o AnyObject
564
+ * @returns AnyObject
565
+ */
546
566
  export declare const pick: {
547
- (a: Placeholder, b: AnyObject): (a: string[]) => {};
548
- (a: string[], b: Placeholder): (b: AnyObject) => {};
549
- (a: string[]): (b: AnyObject) => {};
550
- (a: string[], b: AnyObject): {};
567
+ (a: Placeholder, b: AnyObject): (a: (string | number)[]) => {};
568
+ (a: (string | number)[], b: Placeholder): (b: AnyObject) => {};
569
+ (a: (string | number)[]): (b: AnyObject) => {};
570
+ (a: (string | number)[], b: AnyObject): {};
551
571
  };
552
572
  export declare const pickBy: {
553
573
  (a: Placeholder, b: AnyObject): (a: Cond) => any;
@@ -715,6 +735,12 @@ export declare const weakEq: {
715
735
  (a: any): (b: any) => boolean;
716
736
  (a: any, b: any): boolean;
717
737
  };
738
+ export declare const uniqBy: {
739
+ (a: Placeholder, b: any[]): (a: (x: any, y: any) => boolean) => any;
740
+ (a: (x: any, y: any) => boolean, b: Placeholder): (b: any[]) => any;
741
+ (a: (x: any, y: any) => boolean): (b: any[]) => any;
742
+ (a: (x: any, y: any) => boolean, b: any[]): any;
743
+ };
718
744
  type StrTmpl = ((data: AnyObject) => string);
719
745
  /** Supports ecrans: '\\{"json": {yes} \\}'
720
746
  @returns getTmpl('one{meme}two')({meme: 42}) -> one42two */
package/dist/bundle.mjs CHANGED
@@ -134,9 +134,9 @@ const includes = curry2((s, ss) => {
134
134
  }
135
135
  });
136
136
 
137
- /* Then next fns seem to be excess due to their safe ver performance should be the same or better:
138
- * qflat, qpick, qslice, quniq, qflat, qflatShallow, qreduceAsync
139
- */
137
+ const { min } = Math;
138
+ const z = 0;
139
+ /* qflat, qflatShallow, qreduceAsync */
140
140
  const qappend = curry2((s, xs) => { xs.push(s); return xs; });
141
141
  const qassoc = curry3((prop, v, obj) => { obj[prop] = v; return obj; });
142
142
  const qreduce = curry3((fn, accum, arr) => arr.reduce(fn, accum));
@@ -252,6 +252,55 @@ const qomit = curry2((props, o) => qfilter((_, k) => !includes(k, props), o));
252
252
  /** @param prop string @param pipe (data[prop]): prop_value @param data any
253
253
  * @returns data with prop over pipe. */
254
254
  const qoverProp = curry3((prop, pipe, data) => qassoc(prop, pipe(data[prop]), data));
255
+ /** Slower than pick() (dictionary mode) !
256
+ * @param props (string|number)[]
257
+ * @param o AnyObject
258
+ * @returns AnyObject
259
+ */
260
+ const qpick = curry2((props, o) => {
261
+ for (const p in o)
262
+ if (!props.includes(p))
263
+ delete o[p];
264
+ return o;
265
+ });
266
+ const qslice = curry3((from, to, xs) => {
267
+ const right = (isNum(to) ? to : inf);
268
+ const window_width = min(right, length(xs)) - from;
269
+ if (isArray(xs)) {
270
+ xs = xs;
271
+ if (from > z)
272
+ for (let i = z; i < window_width; i++)
273
+ xs[i] = xs[from + i];
274
+ xs.length = window_width;
275
+ return xs;
276
+ }
277
+ else
278
+ return xs.slice(from, right); // strings are immutable.
279
+ });
280
+ /** Should be faster than .splice() 'cause does not make a new array. */
281
+ const rmel = (index, xs) => {
282
+ const len = length(xs);
283
+ for (let i = index; i < len; i++)
284
+ xs[i] = xs[i + 1];
285
+ xs.length = len - 1;
286
+ return xs;
287
+ };
288
+ const seen = new Set();
289
+ const quniq = (xs) => {
290
+ seen.clear();
291
+ let size = length(xs);
292
+ for (let i = z; i < size; i++) {
293
+ const x = xs[i];
294
+ if (seen.has(x)) {
295
+ rmel(i, xs);
296
+ size--;
297
+ i--;
298
+ }
299
+ else
300
+ seen.add(x);
301
+ }
302
+ return xs;
303
+ };
255
304
  // Aliases.
256
305
  const qpush = qappend;
257
306
 
@@ -378,7 +427,12 @@ const diff = curry2((_xs1, _xs2) => {
378
427
  }
379
428
  return out;
380
429
  });
381
- const genBy = curry2((generator, length) => [...Array(length)].map((_, i) => generator(i)));
430
+ const genBy = curry2((generator, length) => {
431
+ const a = new Array(length);
432
+ for (let i = 0; i < length; i++)
433
+ a[i] = generator(i);
434
+ return a;
435
+ });
382
436
  const once = (fn) => {
383
437
  let done = false, cache;
384
438
  return function (...args) {
@@ -469,6 +523,11 @@ const freezeShallow = (o) => qfreezeShallow(clone(o));
469
523
  * @param array T2[]
470
524
  */
471
525
  const reduce = curry3((reducer, accum, arr) => qreduce(reducer, clone(accum), arr));
526
+ /**
527
+ * @param props (string|number)[]
528
+ * @param o AnyObject
529
+ * @returns AnyObject
530
+ */
472
531
  const pick = curry2((props, o) => {
473
532
  const out = {};
474
533
  for (const p of props)
@@ -552,6 +611,7 @@ const notf = complement;
552
611
  const push = append;
553
612
  const some = any;
554
613
  const weakEq = eq;
614
+ const uniqBy = uniqWith;
555
615
 
556
616
  /** One promise waits for another. */
557
617
  const forEachSerial = (() => {
@@ -663,4 +723,4 @@ const wait = (time) => new Promise((ff) => setTimeout(ff, time));
663
723
  // TODO: possibly introduce a second argument limiting unfolding.
664
724
  const uncurry = (fn) => (...args) => qreduce(((fn, arg) => fn ? fn(arg) : fn), fn, args);
665
725
 
666
- export { F, T, __, add, all, allPass, always, any, anyPass, append, assoc, assocPath, bind, both, callFrom, callWith, clone, cloneShallow, complement, compose, composeAsync, concat, cond, curry, curry2, curry3, debounce, diff, divide, echo, empty, eq, equals, explore, filter, find, findIndex, flat, flatShallow, flatTo, flip, forEach, forEachAsync, forEachSerial, freeze, freezeShallow, fromPairs, genBy, getTmpl, gt, gte, head, identity, ifElse, includes, indexOf, intersection, isEmpty, isNil, join, keys, last, length, lt, lte, map, mapKeys, mapObj, memoize, mergeDeep, mergeDeepAdd, mergeDeepX, mergeShallow, mirror, multiply, noop, not, notf, nth, omit, once, overProp, path, pathEq, pathExists, pathOr, pathsEq, pick, pickBy, prepend, prop, propEq, propsEq, push, qappend, qassoc, qassocPath, qempty, qfilter, qfreeze, qfreezeShallow, qmap, qmapKeys, qmapObj, qmergeDeep, qmergeDeepAdd, qmergeDeepX, qmergeShallow, qomit, qoverProp, qprepend, qpush, qreduce, qreverse, qsort, range, reduce, reflect, replace, reverse, sizeof, slice, some, sort, split, startsWith, startsWithShallow, subtract, symbol, tail, take, tap, test, throttle, toLower, toPairs, toUpper, trim, type, typeIs, uncurry, uniq, uniqWith, values, wait, waitAll, waitTap, weakEq, when, zip, zipObj, zipWith };
726
+ export { F, T, __, add, all, allPass, always, any, anyPass, append, assoc, assocPath, bind, both, callFrom, callWith, clone, cloneShallow, complement, compose, composeAsync, concat, cond, curry, curry2, curry3, debounce, diff, divide, echo, empty, eq, equals, explore, filter, find, findIndex, flat, flatShallow, flatTo, flip, forEach, forEachAsync, forEachSerial, freeze, freezeShallow, fromPairs, genBy, getTmpl, gt, gte, head, identity, ifElse, includes, indexOf, intersection, isEmpty, isNil, join, keys, last, length, lt, lte, map, mapKeys, mapObj, memoize, mergeDeep, mergeDeepAdd, mergeDeepX, mergeShallow, mirror, multiply, noop, not, notf, nth, omit, once, overProp, path, pathEq, pathExists, pathOr, pathsEq, pick, pickBy, prepend, prop, propEq, propsEq, push, qappend, qassoc, qassocPath, qempty, qfilter, qfreeze, qfreezeShallow, qmap, qmapKeys, qmapObj, qmergeDeep, qmergeDeepAdd, qmergeDeepX, qmergeShallow, qomit, qoverProp, qpick, qprepend, qpush, qreduce, qreverse, qslice, qsort, quniq, range, reduce, reflect, replace, reverse, sizeof, slice, some, sort, split, startsWith, startsWithShallow, subtract, symbol, tail, take, tap, test, throttle, toLower, toPairs, toUpper, trim, type, typeIs, uncurry, uniq, uniqBy, uniqWith, values, wait, waitAll, waitTap, weakEq, when, zip, zipObj, zipWith };
package/package.json CHANGED
@@ -41,7 +41,7 @@
41
41
  "prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
42
42
  "all": "npm run dev && npm run prod"
43
43
  },
44
- "version": "1.6.23",
44
+ "version": "1.7.0",
45
45
  "devDependencies": {
46
46
  "@rollup/plugin-commonjs": "^29.0.0",
47
47
  "@rollup/plugin-node-resolve": "^16.0.3",
package/src/bench.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { qpick } from "./quick"
2
+ import { genBy, identity, range } from "./safe"
3
+
4
+
5
+ if(false) {
6
+ // genBy with [...Array(length)].map(...) performs @ average 53.193s/1e5, 303ms/1e4.
7
+ console.time('genBy') // now: 17s/1e5, 65ms/1e4
8
+ for(let i=0; i<1e5; i++) genBy(identity, i)
9
+ console.timeEnd('genBy')
10
+ }
11
+
12
+ // qpick:
13
+ // if o = {1: 'yep', 4: 'yep', 'kakashka': 'noooo', 'anotherkakashka': 'nonono!'}:
14
+ // w/ Set: with range(0, 5) -> 83ms; range(0, 50) -> 189ms.
15
+ // w/o Set: with range(0, 5) -> 69ms; range(0, 50) -> 75ms.
16
+ // if o is generated with 52+ leaset keys are not included in props (generator included!):
17
+ // w/ Set: with range(0, 5) -> 998ms; range(0, 50) -> 1100ms.
18
+ // w/o Set: with range(0, 5) -> 1000ms; range(0, 50) -> 1065ms.
19
+ // pick:
20
+ // if o = {1: 'yep', 4: 'yep', 'kakashka': 'noooo', 'anotherkakashka': 'nonono!'}:
21
+ // w/o Set: with range(0, 5) -> 18ms; range(0, 50) -> 40ms.
22
+ // if o is generated with 52+ leaset keys are not included in props (generator included!):
23
+ // w/o Set: with range(0, 5) -> 600ms; range(0, 50) -> 647ms.
24
+ for(let j=0; j<1e4; j+=1e3) {
25
+ console.time('qpick @'+j)
26
+ const props = range(0, 5)
27
+ for(let i=0; i<1e5; i++) {
28
+ const o = {1: 'yep', 4: 'yep', 'kakashka': 'noooo', 'anotherkakashka': 'nonono!'}
29
+ // for(const x of range(0, 50)) o['_'+x] = x
30
+ qpick(props, o)
31
+ }
32
+ console.timeEnd('qpick @'+j)
33
+ }
package/src/internal.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { curry2 } from "./curry"
2
2
  import { AnyArray, StrLen } from "./internal_types"
3
3
 
4
- export const length = <T extends AnyArray | string>(s: T): T extends string ? StrLen<T> : T["length"] => s.length as any
4
+ export const length = <T extends AnyArray | string>(s: T): T extends string ? StrLen<T> : T['length'] => s.length as any
5
5
  const typed_arr_re = /^(.*?)(8|16|32|64)(Clamped)?Array$/
6
6
  export const is_typed_arr = (t: string) => typed_arr_re.test(t)
7
7
  /** @param start string | any[] @param s string | any[] */
@@ -2,7 +2,7 @@ export type AnyArgs = any[]
2
2
  export type BasicType = 'String'|'Object'|'Number'|'Symbol'|'Array'|'Null'|'Undefined'
3
3
  export type TupleFn<ARG1=any, ARG2=any, Out=any> = (a: ARG1, b: ARG2) => Out
4
4
  export type IDArray = Uint8Array|Uint16Array|Uint32Array
5
- export type AnyArray<T=any> = T[] | readonly T[]
5
+ export type AnyArray<T=any> = T[] | readonly T[] | (ArrayBufferView&{length: number})
6
6
  export type Split<S extends string> = S extends `${infer U}${infer V}` ? [U, ...Split<V>] : []
7
7
  export type IndexesOfArray<A> = Exclude<keyof A, keyof []>
8
8
  export type StrLen<S extends string, Acc extends 0[] = []> =
package/src/quick.ts CHANGED
@@ -1,10 +1,10 @@
1
+ import { includes, length, type } from "./common"
1
2
  import { curry2, curry3 } from "./curry"
2
- import { includes, type } from "./common"
3
- import { AnyObject, Reducer, AnyFunc } from "./types"
4
- import { isFunc, isArray, isObj, isNil } from "./utils"
5
- /* Then next fns seem to be excess due to their safe ver performance should be the same or better:
6
- * qflat, qpick, qslice, quniq, qflat, qflatShallow, qreduceAsync
7
- */
3
+ import { AnyFunc, AnyObject, Reducer } from "./types"
4
+ import { inf, isArray, isFunc, isNil, isNum, isObj } from "./utils"
5
+ const {min} = Math
6
+ const z = 0
7
+ /* qflat, qflatShallow, qreduceAsync */
8
8
 
9
9
  export const qappend = curry2((s: any, xs: any[]) => {xs.push(s); return xs})
10
10
  export const qassoc = curry3((prop: string, v: any, obj: AnyObject) => { obj[prop] = v; return obj })
@@ -132,6 +132,45 @@ export const qomit = curry2(
132
132
  export const qoverProp = curry3(
133
133
  (prop: string, pipe: AnyFunc, data: any) => qassoc(prop, pipe(data[prop]), data)
134
134
  )
135
+ /** Slower than pick() (dictionary mode) !
136
+ * @param props (string|number)[]
137
+ * @param o AnyObject
138
+ * @returns AnyObject
139
+ */
140
+ export const qpick = curry2((props: string[], o: AnyObject) => {
141
+ for(const p in o) if(!props.includes(p)) delete o[p]
142
+ return o
143
+ })
144
+ export const qslice = curry3(
145
+ (from: number, to: number, xs: any[] | string) => {
146
+ const right = (isNum(to)?to:inf) as number
147
+ const window_width = min(right, length(xs))-from
148
+ if(isArray(xs)) {
149
+ xs = xs as any[]
150
+ if(from>z) for(let i=z; i<window_width; i++) xs[i] = xs[from+i]
151
+ xs.length = window_width
152
+ return xs
153
+ } else return xs.slice(from, right) // strings are immutable.
154
+ }
155
+ )
156
+ /** Should be faster than .splice() 'cause does not make a new array. */
157
+ const rmel = (index: number, xs: any[]) => {
158
+ const len = length(xs)
159
+ for(let i=index; i<len; i++) xs[i]=xs[i+1]
160
+ xs.length = len-1
161
+ return xs
162
+ }
163
+ const seen = new Set()
164
+ export const quniq = (xs: any[]) => {
165
+ seen.clear()
166
+ let size = length(xs)
167
+ for(let i=z; i<size; i++) {
168
+ const x = xs[i]
169
+ if(seen.has(x)) {rmel(i, xs); size--; i--}
170
+ else seen.add(x)
171
+ }
172
+ return xs
173
+ }
135
174
 
136
175
  // Aliases.
137
176
  export const qpush = qappend
package/src/safe.ts CHANGED
@@ -198,7 +198,11 @@ export const genBy = curry2(
198
198
  (
199
199
  generator: (i: number) => any,
200
200
  length: number
201
- ) => [...Array(length)].map((_, i) => generator(i))
201
+ ) => {
202
+ const a = new Array(length)
203
+ for(let i=0; i<length; i++) a[i] = generator(i)
204
+ return a
205
+ }
202
206
  )
203
207
  export const once = <Func extends AnyFunc>(fn: Func) => {
204
208
  let done = false, cache: ReturnType<Func>
@@ -314,8 +318,13 @@ export const reduce = curry3(
314
318
  <T = any>(reducer: Reducer<T>, accum: T, arr: any[]) =>
315
319
  qreduce(reducer, clone(accum), arr)
316
320
  )
321
+ /**
322
+ * @param props (string|number)[]
323
+ * @param o AnyObject
324
+ * @returns AnyObject
325
+ */
317
326
  export const pick = curry2(
318
- (props: string[], o: AnyObject) => {
327
+ (props: (string|number)[], o: AnyObject) => {
319
328
  const out = {}
320
329
  for(const p of props) if(p in o) out[p] = o[p]
321
330
  return out
@@ -453,4 +462,5 @@ export const echo = identity
453
462
  export const notf = complement
454
463
  export const push = append
455
464
  export const some = any
456
- export const weakEq = eq
465
+ export const weakEq = eq
466
+ export const uniqBy = uniqWith