@ezez/utils 1.0.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/.prettierignore +3 -0
- package/.prettierrc.json +1 -0
- package/CHANGELOG.md +244 -0
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/babel.config.cjs +6 -0
- package/dist/cap.d.ts +3 -0
- package/dist/cap.d.ts.map +1 -0
- package/dist/cap.js +14 -0
- package/dist/cap.js.map +1 -0
- package/dist/capitalize.d.ts +3 -0
- package/dist/capitalize.d.ts.map +1 -0
- package/dist/capitalize.js +9 -0
- package/dist/capitalize.js.map +1 -0
- package/dist/coalesce.d.ts +3 -0
- package/dist/coalesce.d.ts.map +1 -0
- package/dist/coalesce.js +14 -0
- package/dist/coalesce.js.map +1 -0
- package/dist/ensureArray.d.ts +3 -0
- package/dist/ensureArray.d.ts.map +1 -0
- package/dist/ensureArray.js +11 -0
- package/dist/ensureArray.js.map +1 -0
- package/dist/ensureError.d.ts +3 -0
- package/dist/ensureError.d.ts.map +1 -0
- package/dist/ensureError.js +11 -0
- package/dist/ensureError.js.map +1 -0
- package/dist/get.d.ts +7 -0
- package/dist/get.d.ts.map +1 -0
- package/dist/get.js +19 -0
- package/dist/get.js.map +1 -0
- package/dist/getMultiple.d.ts +7 -0
- package/dist/getMultiple.d.ts.map +1 -0
- package/dist/getMultiple.js +18 -0
- package/dist/getMultiple.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/insertSeparator.d.ts +3 -0
- package/dist/insertSeparator.d.ts.map +1 -0
- package/dist/insertSeparator.js +18 -0
- package/dist/insertSeparator.js.map +1 -0
- package/dist/isEmpty.d.ts +3 -0
- package/dist/isEmpty.d.ts.map +1 -0
- package/dist/isEmpty.js +26 -0
- package/dist/isEmpty.js.map +1 -0
- package/dist/isPlainObject.d.ts +3 -0
- package/dist/isPlainObject.d.ts.map +1 -0
- package/dist/isPlainObject.js +10 -0
- package/dist/isPlainObject.js.map +1 -0
- package/dist/last.d.ts +3 -0
- package/dist/last.d.ts.map +1 -0
- package/dist/last.js +6 -0
- package/dist/last.js.map +1 -0
- package/dist/mapAsync.d.ts +3 -0
- package/dist/mapAsync.d.ts.map +1 -0
- package/dist/mapAsync.js +22 -0
- package/dist/mapAsync.js.map +1 -0
- package/dist/mapValues.d.ts +6 -0
- package/dist/mapValues.d.ts.map +1 -0
- package/dist/mapValues.js +18 -0
- package/dist/mapValues.js.map +1 -0
- package/dist/match.d.ts +8 -0
- package/dist/match.d.ts.map +1 -0
- package/dist/match.js +20 -0
- package/dist/match.js.map +1 -0
- package/dist/merge.d.ts +15 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +28 -0
- package/dist/merge.js.map +1 -0
- package/dist/mostFrequent.d.ts +3 -0
- package/dist/mostFrequent.d.ts.map +1 -0
- package/dist/mostFrequent.js +21 -0
- package/dist/mostFrequent.js.map +1 -0
- package/dist/noop.d.ts +3 -0
- package/dist/noop.d.ts.map +1 -0
- package/dist/noop.js +6 -0
- package/dist/noop.js.map +1 -0
- package/dist/omit.d.ts +5 -0
- package/dist/omit.d.ts.map +1 -0
- package/dist/omit.js +21 -0
- package/dist/omit.js.map +1 -0
- package/dist/package.json +1 -0
- package/dist/pick.d.ts +5 -0
- package/dist/pick.d.ts.map +1 -0
- package/dist/pick.js +20 -0
- package/dist/pick.js.map +1 -0
- package/dist/pull.d.ts +3 -0
- package/dist/pull.d.ts.map +1 -0
- package/dist/pull.js +14 -0
- package/dist/pull.js.map +1 -0
- package/dist/remove.d.ts +3 -0
- package/dist/remove.d.ts.map +1 -0
- package/dist/remove.js +18 -0
- package/dist/remove.js.map +1 -0
- package/dist/rethrow.d.ts +3 -0
- package/dist/rethrow.d.ts.map +1 -0
- package/dist/rethrow.js +8 -0
- package/dist/rethrow.js.map +1 -0
- package/dist/scale.d.ts +3 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/scale.js +8 -0
- package/dist/scale.js.map +1 -0
- package/dist/seq.d.ts +8 -0
- package/dist/seq.d.ts.map +1 -0
- package/dist/seq.js +45 -0
- package/dist/seq.js.map +1 -0
- package/dist/set.d.ts +7 -0
- package/dist/set.d.ts.map +1 -0
- package/dist/set.js +25 -0
- package/dist/set.js.map +1 -0
- package/dist/setImmutable.d.ts +8 -0
- package/dist/setImmutable.d.ts.map +1 -0
- package/dist/setImmutable.js +59 -0
- package/dist/setImmutable.js.map +1 -0
- package/dist/sortBy.d.ts +3 -0
- package/dist/sortBy.d.ts.map +1 -0
- package/dist/sortBy.js +15 -0
- package/dist/sortBy.js.map +1 -0
- package/dist/throttle.d.ts +13 -0
- package/dist/throttle.d.ts.map +1 -0
- package/dist/throttle.js +82 -0
- package/dist/throttle.js.map +1 -0
- package/dist/truthy.d.ts +3 -0
- package/dist/truthy.d.ts.map +1 -0
- package/dist/truthy.js +8 -0
- package/dist/truthy.js.map +1 -0
- package/dist/wait.d.ts +3 -0
- package/dist/wait.d.ts.map +1 -0
- package/dist/wait.js +10 -0
- package/dist/wait.js.map +1 -0
- package/dist/waitFor.d.ts +3 -0
- package/dist/waitFor.d.ts.map +1 -0
- package/dist/waitFor.js +34 -0
- package/dist/waitFor.js.map +1 -0
- package/dist/waitSync.d.ts +3 -0
- package/dist/waitSync.d.ts.map +1 -0
- package/dist/waitSync.js +9 -0
- package/dist/waitSync.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/highlight.css +78 -0
- package/docs/assets/main.js +58 -0
- package/docs/assets/pages.css +14 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1280 -0
- package/docs/functions/cap.html +116 -0
- package/docs/functions/capitalize.html +121 -0
- package/docs/functions/coalesce.html +127 -0
- package/docs/functions/ensureArray.html +114 -0
- package/docs/functions/ensureError.html +110 -0
- package/docs/functions/get.html +138 -0
- package/docs/functions/getMultiple.html +129 -0
- package/docs/functions/insertSeparator.html +123 -0
- package/docs/functions/isEmpty.html +139 -0
- package/docs/functions/isPlainObject.html +108 -0
- package/docs/functions/last.html +126 -0
- package/docs/functions/mapAsync.html +144 -0
- package/docs/functions/mapValues.html +130 -0
- package/docs/functions/match.html +122 -0
- package/docs/functions/merge.html +464 -0
- package/docs/functions/mostFrequent.html +111 -0
- package/docs/functions/noop.html +101 -0
- package/docs/functions/omit.html +129 -0
- package/docs/functions/pick.html +129 -0
- package/docs/functions/pull.html +113 -0
- package/docs/functions/remove.html +129 -0
- package/docs/functions/rethrow.html +106 -0
- package/docs/functions/scale.html +117 -0
- package/docs/functions/seq.html +128 -0
- package/docs/functions/seqEarlyBreak.html +126 -0
- package/docs/functions/set.html +138 -0
- package/docs/functions/setImmutable.html +137 -0
- package/docs/functions/sortBy.html +135 -0
- package/docs/functions/throttle.html +131 -0
- package/docs/functions/truthy.html +118 -0
- package/docs/functions/wait.html +109 -0
- package/docs/functions/waitFor.html +130 -0
- package/docs/functions/waitSync.html +109 -0
- package/docs/index.html +182 -0
- package/docs/interfaces/GetMultipleSource.html +106 -0
- package/docs/interfaces/GetSource.html +106 -0
- package/docs/interfaces/SetImmutableSource.html +106 -0
- package/docs/interfaces/SetSource.html +106 -0
- package/docs/interfaces/ThrottleOptions.html +80 -0
- package/docs/interfaces/ThrottledFunctionExtras.html +96 -0
- package/docs/modules.html +152 -0
- package/docs/pages/Introduction.html +94 -0
- package/docs/types/MapValuesFn.html +134 -0
- package/docs/types/MatchCallback.html +113 -0
- package/docs/types/SeqEarlyBreaker.html +114 -0
- package/docs/types/SeqFn.html +112 -0
- package/docs/types/SeqFunctions.html +105 -0
- package/docs/types/SetImmutablePath.html +99 -0
- package/docs/types/ThrottledFunction.html +113 -0
- package/docs/variables/mapValuesUNSET.html +101 -0
- package/docs/variables/mergeUNSET.html +103 -0
- package/esm/cap.d.ts +3 -0
- package/esm/cap.d.ts.map +1 -0
- package/esm/cap.js +11 -0
- package/esm/cap.js.map +1 -0
- package/esm/capitalize.d.ts +3 -0
- package/esm/capitalize.d.ts.map +1 -0
- package/esm/capitalize.js +6 -0
- package/esm/capitalize.js.map +1 -0
- package/esm/coalesce.d.ts +3 -0
- package/esm/coalesce.d.ts.map +1 -0
- package/esm/coalesce.js +11 -0
- package/esm/coalesce.js.map +1 -0
- package/esm/ensureArray.d.ts +3 -0
- package/esm/ensureArray.d.ts.map +1 -0
- package/esm/ensureArray.js +8 -0
- package/esm/ensureArray.js.map +1 -0
- package/esm/ensureError.d.ts +3 -0
- package/esm/ensureError.d.ts.map +1 -0
- package/esm/ensureError.js +8 -0
- package/esm/ensureError.js.map +1 -0
- package/esm/get.d.ts +7 -0
- package/esm/get.d.ts.map +1 -0
- package/esm/get.js +16 -0
- package/esm/get.js.map +1 -0
- package/esm/getMultiple.d.ts +7 -0
- package/esm/getMultiple.d.ts.map +1 -0
- package/esm/getMultiple.js +15 -0
- package/esm/getMultiple.js.map +1 -0
- package/esm/index.d.ts +33 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +33 -0
- package/esm/index.js.map +1 -0
- package/esm/insertSeparator.d.ts +3 -0
- package/esm/insertSeparator.d.ts.map +1 -0
- package/esm/insertSeparator.js +15 -0
- package/esm/insertSeparator.js.map +1 -0
- package/esm/isEmpty.d.ts +3 -0
- package/esm/isEmpty.d.ts.map +1 -0
- package/esm/isEmpty.js +23 -0
- package/esm/isEmpty.js.map +1 -0
- package/esm/isPlainObject.d.ts +3 -0
- package/esm/isPlainObject.d.ts.map +1 -0
- package/esm/isPlainObject.js +7 -0
- package/esm/isPlainObject.js.map +1 -0
- package/esm/last.d.ts +3 -0
- package/esm/last.d.ts.map +1 -0
- package/esm/last.js +3 -0
- package/esm/last.js.map +1 -0
- package/esm/mapAsync.d.ts +3 -0
- package/esm/mapAsync.d.ts.map +1 -0
- package/esm/mapAsync.js +19 -0
- package/esm/mapAsync.js.map +1 -0
- package/esm/mapValues.d.ts +6 -0
- package/esm/mapValues.d.ts.map +1 -0
- package/esm/mapValues.js +14 -0
- package/esm/mapValues.js.map +1 -0
- package/esm/match.d.ts +8 -0
- package/esm/match.d.ts.map +1 -0
- package/esm/match.js +17 -0
- package/esm/match.js.map +1 -0
- package/esm/merge.d.ts +15 -0
- package/esm/merge.d.ts.map +1 -0
- package/esm/merge.js +24 -0
- package/esm/merge.js.map +1 -0
- package/esm/mostFrequent.d.ts +3 -0
- package/esm/mostFrequent.d.ts.map +1 -0
- package/esm/mostFrequent.js +18 -0
- package/esm/mostFrequent.js.map +1 -0
- package/esm/noop.d.ts +3 -0
- package/esm/noop.d.ts.map +1 -0
- package/esm/noop.js +3 -0
- package/esm/noop.js.map +1 -0
- package/esm/omit.d.ts +5 -0
- package/esm/omit.d.ts.map +1 -0
- package/esm/omit.js +20 -0
- package/esm/omit.js.map +1 -0
- package/esm/pick.d.ts +5 -0
- package/esm/pick.d.ts.map +1 -0
- package/esm/pick.js +17 -0
- package/esm/pick.js.map +1 -0
- package/esm/pull.d.ts +3 -0
- package/esm/pull.d.ts.map +1 -0
- package/esm/pull.js +11 -0
- package/esm/pull.js.map +1 -0
- package/esm/remove.d.ts +3 -0
- package/esm/remove.d.ts.map +1 -0
- package/esm/remove.js +15 -0
- package/esm/remove.js.map +1 -0
- package/esm/rethrow.d.ts +3 -0
- package/esm/rethrow.d.ts.map +1 -0
- package/esm/rethrow.js +5 -0
- package/esm/rethrow.js.map +1 -0
- package/esm/scale.d.ts +3 -0
- package/esm/scale.d.ts.map +1 -0
- package/esm/scale.js +5 -0
- package/esm/scale.js.map +1 -0
- package/esm/seq.d.ts +8 -0
- package/esm/seq.d.ts.map +1 -0
- package/esm/seq.js +41 -0
- package/esm/seq.js.map +1 -0
- package/esm/set.d.ts +7 -0
- package/esm/set.d.ts.map +1 -0
- package/esm/set.js +22 -0
- package/esm/set.js.map +1 -0
- package/esm/setImmutable.d.ts +8 -0
- package/esm/setImmutable.d.ts.map +1 -0
- package/esm/setImmutable.js +56 -0
- package/esm/setImmutable.js.map +1 -0
- package/esm/sortBy.d.ts +3 -0
- package/esm/sortBy.d.ts.map +1 -0
- package/esm/sortBy.js +12 -0
- package/esm/sortBy.js.map +1 -0
- package/esm/throttle.d.ts +13 -0
- package/esm/throttle.d.ts.map +1 -0
- package/esm/throttle.js +79 -0
- package/esm/throttle.js.map +1 -0
- package/esm/truthy.d.ts +3 -0
- package/esm/truthy.d.ts.map +1 -0
- package/esm/truthy.js +5 -0
- package/esm/truthy.js.map +1 -0
- package/esm/wait.d.ts +3 -0
- package/esm/wait.d.ts.map +1 -0
- package/esm/wait.js +7 -0
- package/esm/wait.js.map +1 -0
- package/esm/waitFor.d.ts +3 -0
- package/esm/waitFor.d.ts.map +1 -0
- package/esm/waitFor.js +31 -0
- package/esm/waitFor.js.map +1 -0
- package/esm/waitSync.d.ts +3 -0
- package/esm/waitSync.d.ts.map +1 -0
- package/esm/waitSync.js +6 -0
- package/esm/waitSync.js.map +1 -0
- package/package.json +75 -0
- package/src/cap.spec.ts +36 -0
- package/src/cap.ts +19 -0
- package/src/capitalize.spec.ts +18 -0
- package/src/capitalize.ts +16 -0
- package/src/coalesce.spec.ts +23 -0
- package/src/coalesce.ts +21 -0
- package/src/ensureArray.spec.ts +87 -0
- package/src/ensureArray.ts +13 -0
- package/src/ensureError.spec.ts +29 -0
- package/src/ensureError.ts +15 -0
- package/src/get.spec.ts +183 -0
- package/src/get.ts +53 -0
- package/src/getMultiple.spec.ts +25 -0
- package/src/getMultiple.ts +47 -0
- package/src/index.ts +33 -0
- package/src/insertSeparator.spec.ts +29 -0
- package/src/insertSeparator.ts +22 -0
- package/src/isEmpty.spec.ts +130 -0
- package/src/isEmpty.ts +50 -0
- package/src/isPlainObject.spec.ts +42 -0
- package/src/isPlainObject.ts +18 -0
- package/src/last.spec.ts +88 -0
- package/src/last.ts +12 -0
- package/src/mapAsync.spec.ts +39 -0
- package/src/mapAsync.ts +41 -0
- package/src/mapValues.spec.ts +178 -0
- package/src/mapValues.ts +57 -0
- package/src/match.spec.ts +11 -0
- package/src/match.ts +30 -0
- package/src/merge.spec.ts +69 -0
- package/src/merge.ts +58 -0
- package/src/mostFrequent.spec.ts +35 -0
- package/src/mostFrequent.ts +27 -0
- package/src/noop.ts +8 -0
- package/src/omit.spec.ts +181 -0
- package/src/omit.ts +43 -0
- package/src/pick.spec.ts +168 -0
- package/src/pick.ts +39 -0
- package/src/pull.spec.ts +54 -0
- package/src/pull.ts +18 -0
- package/src/remove.spec.ts +63 -0
- package/src/remove.ts +26 -0
- package/src/rethrow.ts +12 -0
- package/src/scale.ts +18 -0
- package/src/seq.spec.ts +157 -0
- package/src/seq.ts +104 -0
- package/src/set.spec.ts +348 -0
- package/src/set.ts +59 -0
- package/src/setImmutable.spec.ts +241 -0
- package/src/setImmutable.ts +104 -0
- package/src/sortBy.spec.ts +56 -0
- package/src/sortBy.ts +24 -0
- package/src/throttle.spec.ts +136 -0
- package/src/throttle.ts +153 -0
- package/src/truthy.spec.ts +21 -0
- package/src/truthy.ts +13 -0
- package/src/wait.spec.ts +21 -0
- package/src/wait.ts +14 -0
- package/src/waitFor.ts +47 -0
- package/src/waitSync.spec.ts +21 -0
- package/src/waitSync.ts +14 -0
- package/tutorials/Introduction.md +1 -0
package/src/last.spec.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { last as _last } from "lodash";
|
|
2
|
+
|
|
3
|
+
import { last } from "./last.js";
|
|
4
|
+
|
|
5
|
+
const array = [1, 2];
|
|
6
|
+
const holeyArray = [,]; // eslint-disable-line no-sparse-arrays
|
|
7
|
+
const holeyArray2 = Array(2);
|
|
8
|
+
holeyArray2[0] = 1;
|
|
9
|
+
const holeyArray3 = Array(2);
|
|
10
|
+
holeyArray3[1] = 1;
|
|
11
|
+
|
|
12
|
+
const arrayLike = { 0: 0, 1: 1, length: 2 };
|
|
13
|
+
const string = "elo";
|
|
14
|
+
const object = {};
|
|
15
|
+
|
|
16
|
+
const emptyMap = new Map();
|
|
17
|
+
const filledMap = new Map();
|
|
18
|
+
filledMap.set("x", "x");
|
|
19
|
+
|
|
20
|
+
const emptySet = new Set();
|
|
21
|
+
const filledSet = new Set();
|
|
22
|
+
filledSet.add("x");
|
|
23
|
+
|
|
24
|
+
describe("last", () => {
|
|
25
|
+
it("returns last element of typical array, behaves like lodash", () => {
|
|
26
|
+
last(array).must.equal(2);
|
|
27
|
+
_last(array).must.equal(2);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("returns last element of holey array, behaves like lodash", () => {
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
32
|
+
(last(holeyArray) === undefined).must.be.true();
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
34
|
+
(_last(holeyArray) === undefined).must.be.true();
|
|
35
|
+
|
|
36
|
+
(last(holeyArray2) === undefined).must.be.true();
|
|
37
|
+
(_last(holeyArray2) === undefined).must.be.true();
|
|
38
|
+
|
|
39
|
+
last(holeyArray3).must.equal(1);
|
|
40
|
+
_last(holeyArray3).must.equal(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("returns last element of array-like object, behaves like lodash", () => {
|
|
44
|
+
last(arrayLike).must.equal(1);
|
|
45
|
+
_last(arrayLike).must.equal(1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns undefined for standard object, behaves like lodash", () => {
|
|
49
|
+
(last(object) === undefined).must.be.true();
|
|
50
|
+
(_last(object) === undefined).must.be.true();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns last char of a string, behaves like lodash", () => {
|
|
54
|
+
last(string).must.equal("o");
|
|
55
|
+
_last(string).must.equal("o");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("crashes on nil values, behaves NOT like lodash", () => {
|
|
59
|
+
(() => last(null)).must.throw();
|
|
60
|
+
(_last(null) === undefined).must.be.true();
|
|
61
|
+
|
|
62
|
+
(() => last()).must.throw();
|
|
63
|
+
(_last() === undefined).must.be.true();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("returns undefined for other values, behaves like lodash", () => {
|
|
67
|
+
(last(NaN) === undefined).must.be.true();
|
|
68
|
+
(_last(NaN) === undefined).must.be.true();
|
|
69
|
+
(last(1) === undefined).must.be.true();
|
|
70
|
+
(_last(1) === undefined).must.be.true();
|
|
71
|
+
(last(0) === undefined).must.be.true();
|
|
72
|
+
(_last(0) === undefined).must.be.true();
|
|
73
|
+
(last(true) === undefined).must.be.true();
|
|
74
|
+
(_last(true) === undefined).must.be.true();
|
|
75
|
+
(last(false) === undefined).must.be.true();
|
|
76
|
+
(_last(false) === undefined).must.be.true();
|
|
77
|
+
(last(Infinity) === undefined).must.be.true();
|
|
78
|
+
(_last(Infinity) === undefined).must.be.true();
|
|
79
|
+
(last(emptyMap) === undefined).must.be.true();
|
|
80
|
+
(_last(emptyMap) === undefined).must.be.true();
|
|
81
|
+
(last(filledMap) === undefined).must.be.true();
|
|
82
|
+
(_last(filledMap) === undefined).must.be.true();
|
|
83
|
+
(last(emptySet) === undefined).must.be.true();
|
|
84
|
+
(_last(emptySet) === undefined).must.be.true();
|
|
85
|
+
(last(filledSet) === undefined).must.be.true();
|
|
86
|
+
(_last(filledSet) === undefined).must.be.true();
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/last.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets last element of an array.
|
|
3
|
+
*
|
|
4
|
+
* @param {Array} array - source array
|
|
5
|
+
* @example last([1, 2]) // 2
|
|
6
|
+
* @example last([1]) // 1
|
|
7
|
+
* @example last([]) // undefined
|
|
8
|
+
* @returns {*} - last element of an array or undefined
|
|
9
|
+
*/
|
|
10
|
+
const last = <T>(array: T[]): T | undefined => array[array.length - 1];
|
|
11
|
+
|
|
12
|
+
export { last };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { mapAsync } from "./mapAsync.js";
|
|
2
|
+
import { waitSync } from "./waitSync.js";
|
|
3
|
+
|
|
4
|
+
const slowCb = (val: number) => {
|
|
5
|
+
waitSync(5);
|
|
6
|
+
return val + 1;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe("mapAsync", () => {
|
|
10
|
+
it("must return the same output as map", async () => {
|
|
11
|
+
(await mapAsync([1, 2, 3], i => i + 1)).must.eql([2, 3, 4]);
|
|
12
|
+
(await mapAsync([1, 2, 3], slowCb, 1, 1)).must.eql([2, 3, 4]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("must let other tasks to work", async () => {
|
|
16
|
+
let i = 0,
|
|
17
|
+
id;
|
|
18
|
+
const bump = () => {
|
|
19
|
+
i++;
|
|
20
|
+
id = setTimeout(bump, 2);
|
|
21
|
+
// let's make sure we're gonna consume every loop release (timeout lower than wait time) but just once
|
|
22
|
+
// (timeout higher than wait time) - it's just to estimate values while testing
|
|
23
|
+
};
|
|
24
|
+
bump();
|
|
25
|
+
|
|
26
|
+
await mapAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], slowCb, 10);
|
|
27
|
+
|
|
28
|
+
i.must.be.gte(5);
|
|
29
|
+
|
|
30
|
+
// now let's test standard, sync `map`
|
|
31
|
+
i = 0;
|
|
32
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(slowCb);
|
|
33
|
+
i.must.be.equal(0);
|
|
34
|
+
|
|
35
|
+
clearTimeout(id);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// @TODO more tests, test callback params
|
|
39
|
+
});
|
package/src/mapAsync.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { wait } from "./wait.js";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MAX_BLOCK_TIME = 16; // a frame
|
|
4
|
+
const DEFAULT_WAIT_TIME = 1;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Non-blocking version of `Array.prototype.map`, it will pause the map loop every `pauseEvery`. Keep in mind that
|
|
8
|
+
* depending on your code workload the pause interval and duration may be exact or longer than specified. It's the same
|
|
9
|
+
* single thread JavaScript nature setTimeout have to follow.
|
|
10
|
+
* @param {Array} context - original array
|
|
11
|
+
* @param {function} callback - map function
|
|
12
|
+
* @param {number} pauseEvery - pause after this many ms
|
|
13
|
+
* @param {number} pauseTime - how long to wait on pause
|
|
14
|
+
*/
|
|
15
|
+
const mapAsync = async <T, Y>(
|
|
16
|
+
context: readonly T[],
|
|
17
|
+
callback: (item: T, key: number, array: readonly T[]) => Y,
|
|
18
|
+
pauseEvery = DEFAULT_MAX_BLOCK_TIME,
|
|
19
|
+
pauseTime = DEFAULT_WAIT_TIME,
|
|
20
|
+
): Promise<Y[]> => {
|
|
21
|
+
let lastWaitMoment = Date.now();
|
|
22
|
+
const result = [];
|
|
23
|
+
|
|
24
|
+
const l = context.length;
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < l; i++) {
|
|
27
|
+
const item = context[i];
|
|
28
|
+
// eslint-disable-next-line callback-return
|
|
29
|
+
result.push(callback(item, i, context));
|
|
30
|
+
if (Date.now() - lastWaitMoment >= pauseEvery) {
|
|
31
|
+
await wait(pauseTime);
|
|
32
|
+
lastWaitMoment = Date.now();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
mapAsync,
|
|
41
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { mapValues as _mapValues } from "lodash";
|
|
2
|
+
|
|
3
|
+
import { mapValues, mapValuesUNSET } from "./mapValues.js";
|
|
4
|
+
|
|
5
|
+
const REMOVE = mapValuesUNSET;
|
|
6
|
+
|
|
7
|
+
describe("mapValues", () => {
|
|
8
|
+
const square = v => v * v;
|
|
9
|
+
|
|
10
|
+
const obj = {
|
|
11
|
+
a: 5,
|
|
12
|
+
b: 6,
|
|
13
|
+
};
|
|
14
|
+
const result = {
|
|
15
|
+
a: 25,
|
|
16
|
+
b: 36,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
it("returns new object, behaves like lodash", () => {
|
|
20
|
+
mapValues(obj, square).must.not.equal(obj);
|
|
21
|
+
_mapValues(obj, square).must.not.equal(obj);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("maps values, behaves like lodash", () => {
|
|
25
|
+
mapValues(obj, square).must.eql(result);
|
|
26
|
+
_mapValues(obj, square).must.eql(result);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("skips inherited properties, behaves like lodash", () => {
|
|
30
|
+
const Test = function() {};
|
|
31
|
+
Test.prototype.nonOwn = 1;
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
33
|
+
const testInstance = new Test();
|
|
34
|
+
testInstance.own = 2;
|
|
35
|
+
const testInstanceResult = {
|
|
36
|
+
own: 4,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
mapValues(testInstance, square).must.eql(testInstanceResult);
|
|
40
|
+
_mapValues(testInstance, square).must.eql(testInstanceResult);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("skips non-enumerable properties, behaves like lodash", () => {
|
|
44
|
+
const nonEnum = {
|
|
45
|
+
enum: 1,
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(nonEnum, "nonEnum", {
|
|
48
|
+
enumerable: false,
|
|
49
|
+
value: 5,
|
|
50
|
+
});
|
|
51
|
+
const nonEnumResult = {
|
|
52
|
+
enum: 1,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
mapValues(nonEnum, square).must.eql(nonEnumResult);
|
|
56
|
+
_mapValues(nonEnum, square).must.eql(nonEnumResult);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("keeps array an array, behaves NOT like lodash", () => {
|
|
60
|
+
const array = [1, 5];
|
|
61
|
+
array.property = 3;
|
|
62
|
+
const arrayResult = [1, 25];
|
|
63
|
+
arrayResult.property = 9;
|
|
64
|
+
const arrayResultLodash = {
|
|
65
|
+
0: 1,
|
|
66
|
+
1: 25,
|
|
67
|
+
property: 9,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
mapValues(array, square).must.eql(arrayResult);
|
|
71
|
+
_mapValues(array, square).must.eql(arrayResultLodash);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("works on holey array, behaves NOT like lodash", () => {
|
|
75
|
+
const holeyArray = [1,, 3]; // eslint-disable-line no-sparse-arrays
|
|
76
|
+
const holeyArrayResult = [1,, 9]; // eslint-disable-line no-sparse-arrays
|
|
77
|
+
const holeyArrayResultLodash = {
|
|
78
|
+
0: 1,
|
|
79
|
+
1: NaN,
|
|
80
|
+
2: 9,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
mapValues(holeyArray, square).must.eql(holeyArrayResult);
|
|
84
|
+
_mapValues(holeyArray, square).must.eql(holeyArrayResultLodash);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("allows filtering while mapping values", () => {
|
|
88
|
+
const source = {
|
|
89
|
+
title: "abc",
|
|
90
|
+
price: 16.10,
|
|
91
|
+
author: "dzek",
|
|
92
|
+
items: [],
|
|
93
|
+
data: {},
|
|
94
|
+
category: null,
|
|
95
|
+
description: "abcd",
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
mapValues(source, (value, key) => {
|
|
99
|
+
const keyStartsWithCP = ["c", "p"].includes(key.substring(0, 1));
|
|
100
|
+
const isString = typeof value === "string";
|
|
101
|
+
if (!isString && !keyStartsWithCP) {
|
|
102
|
+
return REMOVE;
|
|
103
|
+
}
|
|
104
|
+
if (typeof value === "number") {
|
|
105
|
+
return value + 1;
|
|
106
|
+
}
|
|
107
|
+
if (key === "description") {
|
|
108
|
+
return (value as string).toUpperCase();
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}).must.eql({
|
|
112
|
+
title: "abc",
|
|
113
|
+
price: 17.10,
|
|
114
|
+
author: "dzek",
|
|
115
|
+
category: null,
|
|
116
|
+
description: "ABCD",
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("crashes on nil values, behaves NOT like lodash", () => {
|
|
121
|
+
(() => mapValues(null, square)).must.throw();
|
|
122
|
+
(() => mapValues(undefined, square)).must.throw();
|
|
123
|
+
|
|
124
|
+
(() => _mapValues(null, square)).must.not.throw();
|
|
125
|
+
(() => _mapValues(undefined, square)).must.not.throw();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("calls given method once per property", () => {
|
|
129
|
+
{
|
|
130
|
+
const source = {
|
|
131
|
+
aaa: false,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
let calls;
|
|
135
|
+
calls = 0;
|
|
136
|
+
|
|
137
|
+
mapValues(source, (value) => {
|
|
138
|
+
calls++;
|
|
139
|
+
return value;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
calls.must.equal(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
{
|
|
146
|
+
const source = {
|
|
147
|
+
aaa: false,
|
|
148
|
+
title: "albert eats flowers",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
let calls;
|
|
152
|
+
calls = 0;
|
|
153
|
+
|
|
154
|
+
mapValues(source, (value) => {
|
|
155
|
+
calls++;
|
|
156
|
+
return value;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
calls.must.equal(2);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("leaves gaps in an array if some elements are removed", () => {
|
|
164
|
+
const array = [1, "string", 1, "dog", 69];
|
|
165
|
+
|
|
166
|
+
const res = mapValues(array, (val) => (typeof val === "string" ? REMOVE : val));
|
|
167
|
+
|
|
168
|
+
// eslint-disable-next-line no-sparse-arrays
|
|
169
|
+
res.must.eql([
|
|
170
|
+
1, , 1, , 69,
|
|
171
|
+
]);
|
|
172
|
+
("0" in res).must.be.true();
|
|
173
|
+
("1" in res).must.be.false();
|
|
174
|
+
("2" in res).must.be.true();
|
|
175
|
+
("3" in res).must.be.false();
|
|
176
|
+
("4" in res).must.be.true();
|
|
177
|
+
});
|
|
178
|
+
});
|
package/src/mapValues.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see {@link mapValues}
|
|
3
|
+
*/
|
|
4
|
+
const UNSET = typeof Symbol !== "undefined" ? Symbol("unset") : {};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A callback function to {@link mapValues}. It should return new value, optionally may return exported mapValuesUnset
|
|
8
|
+
* value to unset a property in the target the object.
|
|
9
|
+
*
|
|
10
|
+
* @callback MapValuesFn
|
|
11
|
+
* @param {*} value - property value
|
|
12
|
+
* @param {string} key - property name
|
|
13
|
+
* @example function fn(value, key) { return value * 5; } // all properties will be multiplied
|
|
14
|
+
* @example function fn(value, key) {
|
|
15
|
+
* if (key === "name") {
|
|
16
|
+
* return mapValuesUNSET;
|
|
17
|
+
* }
|
|
18
|
+
* return value.toUpperCase();
|
|
19
|
+
* } // will upper case all properties and filter out name property
|
|
20
|
+
* @returns {*} - new value of a property
|
|
21
|
+
*/
|
|
22
|
+
type MapValuesFn<S, R> = (value: S[keyof S], key: keyof S) => R;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Iterates through object properties returning object with same properties but modified values. Optionally some
|
|
26
|
+
* properties may be filtered out on returned object.
|
|
27
|
+
*
|
|
28
|
+
* @param {Object|Array} source - source object
|
|
29
|
+
* @param {MapValuesFn} fn - map function callback that will return new value of a property
|
|
30
|
+
* @example mapValues({ a: 1, b: 2 }, x => x * 2) // will return { a: 2, b: 4 }
|
|
31
|
+
* @example mapValues({ a: 1, b: 2 }, () => mapValuesUNSET) // will return {}
|
|
32
|
+
* @example mapValues({ a: 1, b: 2 }, (value, key) => key === "b" ? mapValuesUNSET : value * 5) // will return { a: 5 }
|
|
33
|
+
* @returns {Object|Array}
|
|
34
|
+
*/
|
|
35
|
+
const mapValues = <SourceObject extends Record<string, unknown>, PossibleReturnValues>(
|
|
36
|
+
source: SourceObject, fn: MapValuesFn<SourceObject, PossibleReturnValues>,
|
|
37
|
+
): { [P in keyof SourceObject]: PossibleReturnValues } => {
|
|
38
|
+
const keys = Object.keys(source) as (keyof SourceObject)[];
|
|
39
|
+
|
|
40
|
+
// @ts-expect-error TypeScript doesn't work well with this type with reduce
|
|
41
|
+
// @TODO check with ts 5+ or something
|
|
42
|
+
const def: { [P in keyof SourceObject]: PossibleReturnValues } = Array.isArray(source) ? [] : {};
|
|
43
|
+
|
|
44
|
+
return keys.reduce((result, key) => {
|
|
45
|
+
const value = fn(source[key], key);
|
|
46
|
+
if (value !== UNSET) {
|
|
47
|
+
result[key] = value; // eslint-disable-line no-param-reassign
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}, def);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export { mapValues, UNSET as mapValuesUNSET };
|
|
54
|
+
|
|
55
|
+
export type {
|
|
56
|
+
MapValuesFn,
|
|
57
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { match } from "./match.js";
|
|
2
|
+
|
|
3
|
+
describe("match", () => {
|
|
4
|
+
it("must match correctly", () => {
|
|
5
|
+
const items = [1, 500, 3, 15, 200, -100];
|
|
6
|
+
|
|
7
|
+
const { matched, unmatched } = match(items, (item) => item > 15);
|
|
8
|
+
matched.must.eql([500, 200]);
|
|
9
|
+
unmatched.must.eql([1, 3, 15, -100]);
|
|
10
|
+
});
|
|
11
|
+
});
|
package/src/match.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type MatchCallback<T> = (value: T) => boolean;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A `Array.prototype.filter`-like function that splits the results into two groups - matched and unmatched
|
|
5
|
+
* @param {Array} list - original array
|
|
6
|
+
* @param {function} fn - function matching elements
|
|
7
|
+
*/
|
|
8
|
+
const match = <T>(list: T[], fn: MatchCallback<T>) => {
|
|
9
|
+
const matched: T[] = [];
|
|
10
|
+
const unmatched: T[] = [];
|
|
11
|
+
list.forEach(item => {
|
|
12
|
+
if (fn(item)) {
|
|
13
|
+
matched.push(item);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
unmatched.push(item);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
matched,
|
|
21
|
+
unmatched,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
match,
|
|
27
|
+
};
|
|
28
|
+
export type {
|
|
29
|
+
MatchCallback,
|
|
30
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { merge, mergeUNSET } from "./merge.js";
|
|
2
|
+
|
|
3
|
+
describe("merge", () => {
|
|
4
|
+
it("merges two objects", () => {
|
|
5
|
+
merge({ a: true }, { b: false }).must.eql({
|
|
6
|
+
a: true,
|
|
7
|
+
b: false,
|
|
8
|
+
});
|
|
9
|
+
merge({ a: true }, { a: false }).must.eql({
|
|
10
|
+
a: false,
|
|
11
|
+
});
|
|
12
|
+
merge({ c: true }, { a: undefined, c: undefined }).must.eql({
|
|
13
|
+
a: undefined,
|
|
14
|
+
c: undefined,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("merges three objects", () => {
|
|
19
|
+
merge({ a: true }, { b: false }, { c: "maybe" }).must.eql({
|
|
20
|
+
a: true,
|
|
21
|
+
b: false,
|
|
22
|
+
c: "maybe",
|
|
23
|
+
});
|
|
24
|
+
merge({ a: true }, { a: false }, { a: true }).must.eql({
|
|
25
|
+
a: true,
|
|
26
|
+
});
|
|
27
|
+
merge({ c: true }, { a: undefined, c: undefined }, { c: null, a: false }).must.eql({
|
|
28
|
+
a: false,
|
|
29
|
+
c: null,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("allows to unset value", () => {
|
|
34
|
+
const result = merge({ c: true }, { c: mergeUNSET });
|
|
35
|
+
result.must.not.have.property("c");
|
|
36
|
+
result.must.eql({});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("allows to overwrite unset value", () => {
|
|
40
|
+
const result = merge({ c: true }, { c: mergeUNSET }, { c: true });
|
|
41
|
+
result.must.eql({ c: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("rejects arrays", () => {
|
|
45
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, [])).must.throw(TypeError);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("rejects primitive types", () => {
|
|
49
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, "string")).must.throw(TypeError);
|
|
50
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, 555)).must.throw(TypeError);
|
|
51
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, false)).must.throw(TypeError);
|
|
52
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, Symbol("hi"))).must.throw(TypeError);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("rejects functions", () => {
|
|
56
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, () => "hi")).must.throw(TypeError);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("ignores nil", () => {
|
|
60
|
+
(() => merge({ c: true }, { c: mergeUNSET }, { c: true }, undefined, null)).must.not.throw();
|
|
61
|
+
merge({ c: true }, { c: mergeUNSET }, { c: true }, undefined, null).must.eql({
|
|
62
|
+
c: true,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("unsets value if mergeUNSET is present on first object", function() {
|
|
67
|
+
merge({ c: mergeUNSET }).must.eql({});
|
|
68
|
+
});
|
|
69
|
+
});
|
package/src/merge.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Special value that can be set as a value in an object.
|
|
3
|
+
* If this value is merged with previous value - the property in a final object will be removed;
|
|
4
|
+
* @see {@link merge}
|
|
5
|
+
*/
|
|
6
|
+
const UNSET = typeof Symbol !== "undefined" ? Symbol("UNSET") : {};
|
|
7
|
+
|
|
8
|
+
interface Merge {
|
|
9
|
+
<A>(a: A): A;
|
|
10
|
+
<A, B>(a: A, b: B): A & B;
|
|
11
|
+
<A, B, C>(a: A, b: B, c: C): A & B & C;
|
|
12
|
+
<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;
|
|
13
|
+
<A, B, C, D, E>(a: A, b: B, c: C, d: D, e: E): A & B & C & D & E;
|
|
14
|
+
<A, B, C, D, E, F>(a: A, b: B, c: C, d: D, e: E, f: F): A & B & C & D & E & F;
|
|
15
|
+
<A, B, C, D, E, F, G>(a: A, b: B, c: C, d: D, e: E, f: F, g: G): A & B & C & D & E & F & G;
|
|
16
|
+
<A, B, C, D, E, F, G, H>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): A & B & C & D & E & F & G & H;
|
|
17
|
+
<A, B extends object>(a: A, ...args: B[]): unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Shallow merges given objects into new object. It does not mutate any object given. Allows removing properties as
|
|
22
|
+
* well by assigning special `mergeUNSET` value.
|
|
23
|
+
* @example merge({a: 1}, {b: 2}) // {a: 1, b: 2}
|
|
24
|
+
* @example merge({a: 1}, {a: 2}) // {a: 2}
|
|
25
|
+
* @example merge({a: 1}, {a: mergeUNSET}) // {}
|
|
26
|
+
* @param {...object} args - input objects
|
|
27
|
+
* @returns {*}
|
|
28
|
+
*/
|
|
29
|
+
const merge: Merge = <MergeCandidate extends object>(...args: MergeCandidate[]) => {
|
|
30
|
+
const nonObjects = args.some(p => {
|
|
31
|
+
const t = typeof p;
|
|
32
|
+
return t === "string" || t === "number" || t === "symbol" || t === "boolean" || t === "function"
|
|
33
|
+
|| Array.isArray(p);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (nonObjects) {
|
|
37
|
+
throw new TypeError("Only objects are allowed");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const r: { [key: string]: unknown } = {};
|
|
41
|
+
args.forEach((obj) => {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
43
|
+
Object.entries(obj || {}).forEach(([key, value]) => {
|
|
44
|
+
if (value === UNSET) {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
46
|
+
delete r[key];
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
r[key] = value;
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return r;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
merge,
|
|
57
|
+
UNSET as mergeUNSET,
|
|
58
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mostFrequent } from "./mostFrequent.js";
|
|
2
|
+
|
|
3
|
+
describe("mostFrequent", () => {
|
|
4
|
+
it("returns most frequent element", () => {
|
|
5
|
+
mostFrequent([1, 2, 3, 2, 5, 400]).must.equal(2);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("returns first most frequent element if equal top count", () => {
|
|
9
|
+
mostFrequent([1, 2, 3, 2, 5, 400, 400]).must.equal(2);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("returns first value if nothing wins", () => {
|
|
13
|
+
mostFrequent([5, 9, 1000]).must.equal(5);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns undefined if array empty", () => {
|
|
17
|
+
must(mostFrequent([])).equal(undefined);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("compares strictly", () => {
|
|
21
|
+
mostFrequent([1, "1", 1, "1", 2, 2, 2]).must.equal(2);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("works with NaNs", () => {
|
|
25
|
+
// lol, must.js doesn't work with NaNs
|
|
26
|
+
Number.isNaN(mostFrequent([5, NaN, 300, NaN])).must.be.true();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("compares by reference", function() {
|
|
30
|
+
const a = {};
|
|
31
|
+
const b = {};
|
|
32
|
+
|
|
33
|
+
mostFrequent([a, b, b]).must.equal(b);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds most frequent value in array
|
|
3
|
+
* @param {Array} array
|
|
4
|
+
*/
|
|
5
|
+
const mostFrequent = <T>(array: T[]): T => {
|
|
6
|
+
let top = 0,
|
|
7
|
+
topValue = array[0];
|
|
8
|
+
|
|
9
|
+
const map = new Map<T, number>();
|
|
10
|
+
array.forEach(value => {
|
|
11
|
+
if (!map.has(value)) {
|
|
12
|
+
map.set(value, 0);
|
|
13
|
+
}
|
|
14
|
+
const next = map.get(value)! + 1;
|
|
15
|
+
map.set(value, map.get(value)! + 1);
|
|
16
|
+
if (next > top) {
|
|
17
|
+
top = next;
|
|
18
|
+
topValue = value;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return topValue;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
mostFrequent,
|
|
27
|
+
};
|