@weborigami/async-tree 0.5.7 → 0.6.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/browser.js +1 -1
- package/index.ts +31 -35
- package/main.js +1 -2
- package/package.json +4 -7
- package/shared.js +77 -12
- package/src/Tree.js +8 -2
- package/src/drivers/AsyncMap.js +210 -0
- package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +36 -27
- package/src/drivers/{calendarTree.js → CalendarMap.js} +81 -62
- package/src/drivers/ConstantMap.js +30 -0
- package/src/drivers/DeepObjectMap.js +27 -0
- package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
- package/src/drivers/FileMap.js +245 -0
- package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
- package/src/drivers/ObjectMap.js +139 -0
- package/src/drivers/SetMap.js +13 -0
- package/src/drivers/{SiteTree.js → SiteMap.js} +16 -17
- package/src/drivers/SyncMap.js +245 -0
- package/src/jsonKeys.d.ts +2 -2
- package/src/jsonKeys.js +6 -5
- package/src/operations/addNextPrevious.js +35 -36
- package/src/operations/assign.js +30 -21
- package/src/operations/cache.js +29 -35
- package/src/operations/cachedKeyFunctions.js +1 -1
- package/src/operations/calendar.js +5 -0
- package/src/operations/clear.js +13 -12
- package/src/operations/constant.js +5 -0
- package/src/operations/deepEntries.js +23 -0
- package/src/operations/deepMap.js +9 -9
- package/src/operations/deepMerge.js +36 -25
- package/src/operations/deepReverse.js +23 -16
- package/src/operations/deepTake.js +7 -7
- package/src/operations/deepText.js +4 -4
- package/src/operations/deepValues.js +3 -6
- package/src/operations/deepValuesIterator.js +11 -11
- package/src/operations/delete.js +8 -12
- package/src/operations/entries.js +17 -10
- package/src/operations/filter.js +9 -7
- package/src/operations/first.js +12 -10
- package/src/operations/forEach.js +10 -13
- package/src/operations/from.js +31 -39
- package/src/operations/globKeys.js +22 -17
- package/src/operations/group.js +2 -2
- package/src/operations/groupBy.js +24 -22
- package/src/operations/has.js +7 -9
- package/src/operations/indent.js +2 -2
- package/src/operations/inners.js +19 -15
- package/src/operations/invokeFunctions.js +22 -10
- package/src/operations/isAsyncMutableTree.js +5 -12
- package/src/operations/isAsyncTree.js +5 -20
- package/src/operations/isMap.js +39 -0
- package/src/operations/isMaplike.js +34 -0
- package/src/operations/isReadOnlyMap.js +14 -0
- package/src/operations/isTraversable.js +3 -3
- package/src/operations/isTreelike.js +5 -30
- package/src/operations/json.js +4 -12
- package/src/operations/keys.js +17 -8
- package/src/operations/length.js +9 -8
- package/src/operations/map.js +27 -30
- package/src/operations/mapExtension.js +20 -16
- package/src/operations/mapReduce.js +22 -17
- package/src/operations/mask.js +31 -22
- package/src/operations/match.js +13 -9
- package/src/operations/merge.js +43 -35
- package/src/operations/paginate.js +26 -18
- package/src/operations/parent.js +7 -7
- package/src/operations/paths.js +8 -8
- package/src/operations/plain.js +6 -6
- package/src/operations/regExpKeys.js +21 -12
- package/src/operations/reverse.js +21 -15
- package/src/operations/root.js +6 -5
- package/src/operations/scope.js +31 -26
- package/src/operations/shuffle.js +23 -16
- package/src/operations/size.js +13 -0
- package/src/operations/sort.js +55 -40
- package/src/operations/sync.js +21 -0
- package/src/operations/take.js +23 -11
- package/src/operations/text.js +4 -4
- package/src/operations/toFunction.js +7 -7
- package/src/operations/traverse.js +4 -4
- package/src/operations/traverseOrThrow.js +13 -9
- package/src/operations/traversePath.js +2 -2
- package/src/operations/values.js +18 -9
- package/src/operations/withKeys.js +22 -16
- package/src/symbols.js +1 -0
- package/src/utilities/castArraylike.js +10 -2
- package/src/utilities/getMapArgument.js +38 -0
- package/src/utilities/getParent.js +2 -2
- package/src/utilities/isStringlike.js +7 -5
- package/src/utilities/setParent.js +7 -7
- package/src/utilities/toFunction.js +2 -2
- package/src/utilities/toPlainValue.js +22 -18
- package/test/SampleAsyncMap.js +34 -0
- package/test/browser/assert.js +20 -0
- package/test/browser/index.html +54 -21
- package/test/drivers/AsyncMap.test.js +119 -0
- package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +42 -23
- package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
- package/test/drivers/ConstantMap.test.js +15 -0
- package/test/drivers/DeepObjectMap.test.js +36 -0
- package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
- package/test/drivers/FileMap.test.js +185 -0
- package/test/drivers/FunctionMap.test.js +56 -0
- package/test/drivers/ObjectMap.test.js +166 -0
- package/test/drivers/SetMap.test.js +35 -0
- package/test/drivers/{SiteTree.test.js → SiteMap.test.js} +14 -10
- package/test/drivers/SyncMap.test.js +321 -0
- package/test/jsonKeys.test.js +2 -2
- package/test/operations/addNextPrevious.test.js +3 -2
- package/test/operations/assign.test.js +30 -35
- package/test/operations/cache.test.js +8 -6
- package/test/operations/cachedKeyFunctions.test.js +6 -5
- package/test/operations/clear.test.js +6 -27
- package/test/operations/deepEntries.test.js +32 -0
- package/test/operations/deepMerge.test.js +6 -5
- package/test/operations/deepReverse.test.js +2 -2
- package/test/operations/deepTake.test.js +2 -2
- package/test/operations/deepText.test.js +4 -4
- package/test/operations/deepValuesIterator.test.js +2 -2
- package/test/operations/delete.test.js +2 -2
- package/test/operations/extensionKeyFunctions.test.js +6 -5
- package/test/operations/filter.test.js +3 -3
- package/test/operations/from.test.js +23 -31
- package/test/operations/globKeys.test.js +9 -9
- package/test/operations/groupBy.test.js +6 -5
- package/test/operations/inners.test.js +4 -4
- package/test/operations/invokeFunctions.test.js +2 -2
- package/test/operations/isMap.test.js +15 -0
- package/test/operations/isMaplike.test.js +15 -0
- package/test/operations/json.test.js +2 -2
- package/test/operations/keys.test.js +16 -3
- package/test/operations/map.test.js +20 -18
- package/test/operations/mapExtension.test.js +6 -6
- package/test/operations/mapReduce.test.js +2 -2
- package/test/operations/mask.test.js +4 -3
- package/test/operations/match.test.js +2 -2
- package/test/operations/merge.test.js +15 -11
- package/test/operations/paginate.test.js +5 -5
- package/test/operations/parent.test.js +3 -3
- package/test/operations/paths.test.js +6 -6
- package/test/operations/plain.test.js +8 -8
- package/test/operations/regExpKeys.test.js +12 -11
- package/test/operations/reverse.test.js +4 -3
- package/test/operations/scope.test.js +6 -5
- package/test/operations/shuffle.test.js +3 -2
- package/test/operations/sort.test.js +7 -10
- package/test/operations/sync.test.js +43 -0
- package/test/operations/take.test.js +2 -2
- package/test/operations/toFunction.test.js +2 -2
- package/test/operations/traverse.test.js +4 -5
- package/test/operations/withKeys.test.js +2 -2
- package/test/utilities/setParent.test.js +6 -6
- package/test/utilities/toFunction.test.js +2 -2
- package/test/utilities/toPlainValue.test.js +51 -12
- package/src/drivers/DeepMapTree.js +0 -23
- package/src/drivers/DeepObjectTree.js +0 -18
- package/src/drivers/DeferredTree.js +0 -81
- package/src/drivers/FileTree.js +0 -276
- package/src/drivers/MapTree.js +0 -70
- package/src/drivers/ObjectTree.js +0 -158
- package/src/drivers/SetTree.js +0 -34
- package/src/drivers/constantTree.js +0 -19
- package/src/drivers/limitConcurrency.js +0 -63
- package/src/internal.js +0 -16
- package/src/utilities/getTreeArgument.js +0 -43
- package/test/drivers/DeepMapTree.test.js +0 -17
- package/test/drivers/DeepObjectTree.test.js +0 -35
- package/test/drivers/DeferredTree.test.js +0 -22
- package/test/drivers/FileTree.test.js +0 -192
- package/test/drivers/FunctionTree.test.js +0 -46
- package/test/drivers/MapTree.test.js +0 -59
- package/test/drivers/ObjectTree.test.js +0 -163
- package/test/drivers/SetTree.test.js +0 -44
- package/test/drivers/constantTree.test.js +0 -13
- package/test/drivers/limitConcurrency.test.js +0 -41
- package/test/operations/isAsyncMutableTree.test.js +0 -17
- package/test/operations/isAsyncTree.test.js +0 -26
- package/test/operations/isTreelike.test.js +0 -13
|
@@ -1,23 +1,27 @@
|
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
1
3
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
-
import
|
|
4
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
5
|
+
import keys from "./keys.js";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
|
-
* Return a new grouping of the
|
|
8
|
+
* Return a new grouping of the map's values into chunks of the specified
|
|
6
9
|
* size.
|
|
7
10
|
*
|
|
8
|
-
* @typedef {import("
|
|
9
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
11
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
10
12
|
*
|
|
11
|
-
* @param {
|
|
13
|
+
* @param {Maplike} maplike
|
|
12
14
|
* @param {number} [size=10]
|
|
13
15
|
*/
|
|
14
|
-
export default async function paginate(
|
|
15
|
-
const
|
|
16
|
+
export default async function paginate(maplike, size = 10) {
|
|
17
|
+
const source = await getMapArgument(maplike, "paginate");
|
|
16
18
|
|
|
17
|
-
const
|
|
18
|
-
const pageCount = Math.ceil(
|
|
19
|
+
const treeKeys = await keys(source);
|
|
20
|
+
const pageCount = Math.ceil(treeKeys.length / size);
|
|
21
|
+
|
|
22
|
+
const paginated = Object.assign(new AsyncMap(), {
|
|
23
|
+
description: "paginate",
|
|
19
24
|
|
|
20
|
-
const paginated = {
|
|
21
25
|
async get(pageKey) {
|
|
22
26
|
const normalized = trailingSlash.remove(pageKey);
|
|
23
27
|
// Note: page numbers are 1-based.
|
|
@@ -27,14 +31,14 @@ export default async function paginate(treelike, size = 10) {
|
|
|
27
31
|
}
|
|
28
32
|
const nextPage = pageNumber + 1 <= pageCount ? pageNumber + 1 : null;
|
|
29
33
|
const previousPage = pageNumber - 1 >= 1 ? pageNumber - 1 : null;
|
|
30
|
-
const items = new
|
|
34
|
+
const items = new SyncMap();
|
|
31
35
|
for (
|
|
32
36
|
let index = (pageNumber - 1) * size;
|
|
33
|
-
index < Math.min(
|
|
37
|
+
index < Math.min(treeKeys.length, pageNumber * size);
|
|
34
38
|
index++
|
|
35
39
|
) {
|
|
36
|
-
const key =
|
|
37
|
-
items.set(key, await
|
|
40
|
+
const key = treeKeys[index];
|
|
41
|
+
items.set(key, await source.get(treeKeys[index]));
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
return {
|
|
@@ -46,11 +50,15 @@ export default async function paginate(treelike, size = 10) {
|
|
|
46
50
|
};
|
|
47
51
|
},
|
|
48
52
|
|
|
49
|
-
async keys() {
|
|
50
|
-
// Return
|
|
51
|
-
|
|
53
|
+
async *keys() {
|
|
54
|
+
// Return from 1..totalPages
|
|
55
|
+
yield* Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
52
56
|
},
|
|
53
|
-
|
|
57
|
+
|
|
58
|
+
source: source,
|
|
59
|
+
|
|
60
|
+
trailingSlashKeys: false,
|
|
61
|
+
});
|
|
54
62
|
|
|
55
63
|
return paginated;
|
|
56
64
|
}
|
package/src/operations/parent.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Returns the parent of the
|
|
4
|
+
* Returns the parent of the map, if any.
|
|
5
5
|
*
|
|
6
|
-
* @typedef {import("../../index.ts").
|
|
6
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
7
7
|
*
|
|
8
|
-
* @param {
|
|
8
|
+
* @param {Maplike} maplike
|
|
9
9
|
*/
|
|
10
|
-
export default async function parent(
|
|
11
|
-
const
|
|
12
|
-
return
|
|
10
|
+
export default async function parent(maplike) {
|
|
11
|
+
const map = await getMapArgument(maplike, "parent");
|
|
12
|
+
return "parent" in map ? map.parent : undefined;
|
|
13
13
|
}
|
package/src/operations/paths.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import isMap from "./isMap.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns slash-separated paths for all values in the tree.
|
|
@@ -10,17 +10,17 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
10
10
|
* If `assumeSlashes` is true, then keys are assumed to have trailing slashes to
|
|
11
11
|
* indicate subtrees. The default value of this option is false.
|
|
12
12
|
*
|
|
13
|
-
* @typedef {import("../../index.ts").
|
|
13
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
14
14
|
*
|
|
15
|
-
* @param {
|
|
15
|
+
* @param {Maplike} maplike
|
|
16
16
|
* @param {{ assumeSlashes?: boolean, base?: string }} options
|
|
17
17
|
*/
|
|
18
|
-
export default async function paths(
|
|
19
|
-
const tree =
|
|
18
|
+
export default async function paths(maplike, options = {}) {
|
|
19
|
+
const tree = await getMapArgument(maplike, "paths");
|
|
20
20
|
const base = options.base ?? "";
|
|
21
21
|
const assumeSlashes = options.assumeSlashes ?? false;
|
|
22
22
|
const result = [];
|
|
23
|
-
for (const key of
|
|
23
|
+
for await (const key of tree.keys()) {
|
|
24
24
|
const separator = trailingSlash.has(base) ? "" : "/";
|
|
25
25
|
const valuePath = base ? `${base}${separator}${key}` : key;
|
|
26
26
|
let isSubtree;
|
|
@@ -38,7 +38,7 @@ export default async function paths(treelike, options = {}) {
|
|
|
38
38
|
}
|
|
39
39
|
if (value) {
|
|
40
40
|
// If we got the value we can check if it's a subtree
|
|
41
|
-
isSubtree =
|
|
41
|
+
isSubtree = isMap(value);
|
|
42
42
|
}
|
|
43
43
|
if (isSubtree) {
|
|
44
44
|
const subPaths = await paths(value, { assumeSlashes, base: valuePath });
|
package/src/operations/plain.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
import toPlainValue from "../utilities/toPlainValue.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -20,15 +20,15 @@ import toPlainValue from "../utilities/toPlainValue.js";
|
|
|
20
20
|
* array `["b", "a", "c" ]` because the tree has the keys in that order. The
|
|
21
21
|
* specific values of the keys (0, 1, and 2) are ignored.
|
|
22
22
|
*
|
|
23
|
-
* @typedef {import("../../index.ts").
|
|
23
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
24
24
|
* @typedef {import("../../index.ts").PlainObject} PlainObject
|
|
25
25
|
*
|
|
26
|
-
* @param {
|
|
26
|
+
* @param {Maplike} maplike
|
|
27
27
|
*/
|
|
28
|
-
export default async function plain(
|
|
29
|
-
if (
|
|
28
|
+
export default async function plain(maplike) {
|
|
29
|
+
if (maplike instanceof Function) {
|
|
30
30
|
throw new TypeError("plain: can't convert a function to a plain object");
|
|
31
31
|
}
|
|
32
|
-
const tree = await
|
|
32
|
+
const tree = await getMapArgument(maplike, "plain");
|
|
33
33
|
return toPlainValue(tree);
|
|
34
34
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
1
3
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
5
|
+
import isMap from "./isMap.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* A tree whose keys are strings interpreted as regular expressions.
|
|
@@ -10,16 +12,19 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
10
12
|
* taken to match the entire key -- if they do not already start and end with
|
|
11
13
|
* `^` and `$` respectively, those are added.
|
|
12
14
|
*
|
|
13
|
-
* @
|
|
15
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
16
|
+
*
|
|
17
|
+
* @param {Maplike} maplike
|
|
18
|
+
* @returns {Promise<AsyncMap>}
|
|
14
19
|
*/
|
|
15
|
-
export default async function regExpKeys(
|
|
16
|
-
const
|
|
20
|
+
export default async function regExpKeys(maplike) {
|
|
21
|
+
const source = await getMapArgument(maplike, "regExpKeys", { deep: true });
|
|
17
22
|
|
|
18
|
-
const map = new
|
|
23
|
+
const map = new SyncMap();
|
|
19
24
|
|
|
20
25
|
// We build the output tree first so that we can refer to it when setting
|
|
21
26
|
// `parent` on subtrees below.
|
|
22
|
-
let result = {
|
|
27
|
+
let result = Object.assign(new AsyncMap(), {
|
|
23
28
|
// @ts-ignore
|
|
24
29
|
description: "regExpKeys",
|
|
25
30
|
|
|
@@ -39,24 +44,28 @@ export default async function regExpKeys(treelike) {
|
|
|
39
44
|
return undefined;
|
|
40
45
|
},
|
|
41
46
|
|
|
42
|
-
async keys() {
|
|
47
|
+
async *keys() {
|
|
43
48
|
return map.keys();
|
|
44
49
|
},
|
|
45
|
-
|
|
50
|
+
|
|
51
|
+
source: source,
|
|
52
|
+
|
|
53
|
+
trailingSlashKeys: false,
|
|
54
|
+
});
|
|
46
55
|
|
|
47
56
|
// Turn the input tree's string keys into regular expressions, then map those
|
|
48
57
|
// to the corresponding values.
|
|
49
|
-
for (const key of
|
|
58
|
+
for await (const key of source.keys()) {
|
|
50
59
|
if (typeof key !== "string") {
|
|
51
60
|
// Skip non-string keys.
|
|
52
61
|
continue;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
// Get value.
|
|
56
|
-
let value = await
|
|
65
|
+
let value = await source.get(key);
|
|
57
66
|
|
|
58
67
|
let regExp;
|
|
59
|
-
if (trailingSlash.has(key) ||
|
|
68
|
+
if (trailingSlash.has(key) || isMap(value)) {
|
|
60
69
|
const baseKey = trailingSlash.remove(key);
|
|
61
70
|
regExp = new RegExp("^" + baseKey + "/?$");
|
|
62
71
|
// Subtree
|
|
@@ -1,26 +1,32 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import keys from "./keys.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
6
|
+
* Return a new map with the keys reversed.
|
|
5
7
|
*
|
|
6
|
-
* @typedef
|
|
7
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
9
|
*
|
|
9
|
-
* @param {
|
|
10
|
-
* @returns {Promise<
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
* @returns {Promise<AsyncMap>}
|
|
11
12
|
*/
|
|
12
|
-
export default async function reverse(
|
|
13
|
-
const
|
|
13
|
+
export default async function reverse(maplike) {
|
|
14
|
+
const source = await getMapArgument(maplike, "reverse");
|
|
15
|
+
return Object.assign(new AsyncMap(), {
|
|
16
|
+
description: "reverse",
|
|
14
17
|
|
|
15
|
-
return {
|
|
16
18
|
async get(key) {
|
|
17
|
-
return
|
|
19
|
+
return source.get(key);
|
|
18
20
|
},
|
|
19
21
|
|
|
20
|
-
async keys() {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
async *keys() {
|
|
23
|
+
const treeKeys = await keys(source);
|
|
24
|
+
treeKeys.reverse();
|
|
25
|
+
yield* treeKeys;
|
|
24
26
|
},
|
|
25
|
-
|
|
27
|
+
|
|
28
|
+
source,
|
|
29
|
+
|
|
30
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
31
|
+
});
|
|
26
32
|
}
|
package/src/operations/root.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import * as symbols from "../symbols.js";
|
|
2
|
-
import
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Walk up the `parent` chain to find the root of the tree.
|
|
6
6
|
*
|
|
7
|
-
* @typedef {import("../../index.ts").
|
|
7
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
8
|
*
|
|
9
|
-
* @param {
|
|
9
|
+
* @param {Maplike} maplike
|
|
10
10
|
*/
|
|
11
|
-
export default function root(
|
|
12
|
-
|
|
11
|
+
export default function root(maplike) {
|
|
12
|
+
/** @type {any} */
|
|
13
|
+
let current = getMapArgument(maplike, "root");
|
|
13
14
|
while (current.parent || current[symbols.parent]) {
|
|
14
15
|
current = current.parent || current[symbols.parent];
|
|
15
16
|
}
|
package/src/operations/scope.js
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* A
|
|
5
|
+
* A map's "scope" is the collection of everything in that map and all of its
|
|
5
6
|
* ancestors.
|
|
6
7
|
*
|
|
7
|
-
* @typedef {import("
|
|
8
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
9
|
*
|
|
10
|
-
* @param {
|
|
11
|
-
* @returns {Promise<
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
* @returns {Promise<AsyncMap>}
|
|
12
12
|
*/
|
|
13
|
-
export default async function scope(
|
|
14
|
-
const
|
|
13
|
+
export default async function scope(maplike) {
|
|
14
|
+
const source = await getMapArgument(maplike, "scope");
|
|
15
15
|
|
|
16
|
-
return {
|
|
17
|
-
|
|
16
|
+
return Object.assign(new AsyncMap(), {
|
|
17
|
+
description: "scope",
|
|
18
|
+
|
|
19
|
+
// Starting with this map, search up the parent hierarchy.
|
|
18
20
|
async get(key) {
|
|
19
|
-
/** @type {
|
|
20
|
-
let current =
|
|
21
|
+
/** @type {Map|AsyncMap|null} */
|
|
22
|
+
let current = source;
|
|
21
23
|
let value;
|
|
22
24
|
while (current) {
|
|
23
25
|
value = await current.get(key);
|
|
24
26
|
if (value !== undefined) {
|
|
25
27
|
break;
|
|
26
28
|
}
|
|
27
|
-
current = current.parent;
|
|
29
|
+
current = "parent" in current ? current.parent : null;
|
|
28
30
|
}
|
|
29
31
|
return value;
|
|
30
32
|
},
|
|
31
33
|
|
|
32
34
|
// Collect all keys for this tree and all parents
|
|
33
|
-
async keys() {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let current = tree;
|
|
35
|
+
async *keys() {
|
|
36
|
+
const scopeKeys = new Set();
|
|
37
|
+
/** @type {Map|AsyncMap|null} */
|
|
38
|
+
let current = source;
|
|
38
39
|
while (current) {
|
|
39
|
-
for (const key of
|
|
40
|
-
|
|
40
|
+
for await (const key of current.keys()) {
|
|
41
|
+
scopeKeys.add(key);
|
|
41
42
|
}
|
|
42
|
-
current = current.parent;
|
|
43
|
+
current = "parent" in current ? current.parent : null;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
yield* scopeKeys;
|
|
46
47
|
},
|
|
47
48
|
|
|
48
49
|
// Collect all keys for this tree and all parents.
|
|
@@ -52,14 +53,18 @@ export default async function scope(treelike) {
|
|
|
52
53
|
get trees() {
|
|
53
54
|
const result = [];
|
|
54
55
|
|
|
55
|
-
/** @type {
|
|
56
|
-
let current =
|
|
56
|
+
/** @type {Map|AsyncMap|null} */
|
|
57
|
+
let current = source;
|
|
57
58
|
while (current) {
|
|
58
59
|
result.push(current);
|
|
59
|
-
current = current.parent;
|
|
60
|
+
current = "parent" in current ? current.parent : null;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
return result;
|
|
63
64
|
},
|
|
64
|
-
|
|
65
|
+
|
|
66
|
+
source: source,
|
|
67
|
+
|
|
68
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
69
|
+
});
|
|
65
70
|
}
|
|
@@ -1,33 +1,40 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import keys from "./keys.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Return a new tree with the original's keys shuffled
|
|
5
7
|
*
|
|
6
|
-
* @typedef
|
|
7
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
9
|
*
|
|
9
|
-
* @param {
|
|
10
|
+
* @param {Maplike} maplike
|
|
10
11
|
* @param {boolean?} reshuffle
|
|
11
|
-
* @returns {Promise<
|
|
12
|
+
* @returns {Promise<AsyncMap>}
|
|
12
13
|
*/
|
|
13
|
-
export default async function shuffle(
|
|
14
|
-
const
|
|
14
|
+
export default async function shuffle(maplike, reshuffle = false) {
|
|
15
|
+
const source = await getMapArgument(maplike, "shuffle");
|
|
15
16
|
|
|
16
|
-
let
|
|
17
|
+
let mapKeys;
|
|
18
|
+
|
|
19
|
+
return Object.assign(new AsyncMap(), {
|
|
20
|
+
description: "shuffle",
|
|
17
21
|
|
|
18
|
-
return {
|
|
19
22
|
async get(key) {
|
|
20
|
-
return
|
|
23
|
+
return source.get(key);
|
|
21
24
|
},
|
|
22
25
|
|
|
23
|
-
async keys() {
|
|
24
|
-
if (!
|
|
25
|
-
|
|
26
|
-
shuffleArray(
|
|
26
|
+
async *keys() {
|
|
27
|
+
if (!mapKeys || reshuffle) {
|
|
28
|
+
mapKeys = await keys(source);
|
|
29
|
+
shuffleArray(mapKeys);
|
|
27
30
|
}
|
|
28
|
-
|
|
31
|
+
yield* mapKeys;
|
|
29
32
|
},
|
|
30
|
-
|
|
33
|
+
|
|
34
|
+
source,
|
|
35
|
+
|
|
36
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
37
|
+
});
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
/*
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Return the number of keys in the map.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
7
|
+
*
|
|
8
|
+
* @param {Maplike} maplike
|
|
9
|
+
*/
|
|
10
|
+
export default async function size(maplike) {
|
|
11
|
+
const map = await getMapArgument(maplike, "size");
|
|
12
|
+
return map.size;
|
|
13
|
+
}
|
package/src/operations/sort.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import keys from "./keys.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
* Return a new
|
|
6
|
+
* Return a new map with the original's keys sorted. A comparison function can
|
|
5
7
|
* be provided; by default the keys will be sorted in [natural sort
|
|
6
8
|
* order](https://en.wikipedia.org/wiki/Natural_sort_order).
|
|
7
9
|
*
|
|
8
|
-
* @typedef {import("
|
|
9
|
-
* @typedef {(key: any,
|
|
10
|
+
* @typedef {import("../../index.ts").SyncOrAsyncMap} SyncOrAsyncMap
|
|
11
|
+
* @typedef {(key: any, map: SyncOrAsyncMap) => any} SortKeyFn
|
|
10
12
|
* @typedef {{ compare?: (a: any, b: any) => number, sortKey?: SortKeyFn }} SortOptions
|
|
11
|
-
* @typedef {import("../../index.ts").
|
|
13
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
12
14
|
* @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
|
|
13
15
|
*
|
|
14
|
-
* @param {
|
|
16
|
+
* @param {Maplike} maplike
|
|
15
17
|
* @param {SortOptions|ValueKeyFn} [options]
|
|
16
18
|
*/
|
|
17
|
-
export default async function sort(
|
|
18
|
-
const
|
|
19
|
+
export default async function sort(maplike, options) {
|
|
20
|
+
const source = await getMapArgument(maplike, "sort");
|
|
19
21
|
|
|
20
22
|
let sortKey;
|
|
21
23
|
let compare;
|
|
@@ -27,39 +29,52 @@ export default async function sort(treelike, options) {
|
|
|
27
29
|
sortKey = options?.sortKey;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
const transformed = Object.
|
|
31
|
-
|
|
32
|
-
const keys = Array.from(await tree.keys());
|
|
32
|
+
const transformed = Object.assign(new AsyncMap(), {
|
|
33
|
+
descriptor: "sort",
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
async get(key) {
|
|
36
|
+
return source.get(key);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async *keys() {
|
|
40
|
+
const treeKeys = await keys(source);
|
|
41
|
+
|
|
42
|
+
let resultKeys;
|
|
43
|
+
if (sortKey) {
|
|
44
|
+
// Invoke the async sortKey function to get sort keys.
|
|
45
|
+
// Create { key, sortKey } tuples.
|
|
46
|
+
const tuples = await Promise.all(
|
|
47
|
+
treeKeys.map(async (key) => {
|
|
48
|
+
const value = await source.get(key);
|
|
49
|
+
const sort = await sortKey(value, key, source);
|
|
50
|
+
if (sort === undefined) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`sortKey function returned undefined for key ${key}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return { key, sort };
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Wrap the comparison function so it applies to sort keys.
|
|
60
|
+
const defaultCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
61
|
+
const originalCompare = compare ?? defaultCompare;
|
|
62
|
+
// Sort by the sort key.
|
|
63
|
+
tuples.sort((a, b) => originalCompare(a.sort, b.sort));
|
|
64
|
+
// Map back to the original keys.
|
|
65
|
+
resultKeys = tuples.map((pair) => pair.key);
|
|
66
|
+
} else {
|
|
67
|
+
// Use original keys as sort keys.
|
|
68
|
+
// If compare is undefined, this uses default sort order.
|
|
69
|
+
resultKeys = treeKeys.slice().sort(compare);
|
|
70
|
+
}
|
|
71
|
+
yield* resultKeys;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
source,
|
|
75
|
+
|
|
76
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
77
|
+
});
|
|
49
78
|
|
|
50
|
-
// Wrap the comparison function so it applies to sort keys.
|
|
51
|
-
const defaultCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
52
|
-
const originalCompare = compare ?? defaultCompare;
|
|
53
|
-
// Sort by the sort key.
|
|
54
|
-
tuples.sort((a, b) => originalCompare(a.sort, b.sort));
|
|
55
|
-
// Map back to the original keys.
|
|
56
|
-
const sorted = tuples.map((pair) => pair.key);
|
|
57
|
-
return sorted;
|
|
58
|
-
} else {
|
|
59
|
-
// Use original keys as sort keys.
|
|
60
|
-
// If compare is undefined, this uses default sort order.
|
|
61
|
-
return keys.slice().sort(compare);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
79
|
return transformed;
|
|
65
80
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import mapReduce from "./mapReduce.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the async tree to a synchronous tree.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
|
+
*
|
|
10
|
+
* @param {Maplike} source
|
|
11
|
+
*/
|
|
12
|
+
export default async function sync(source) {
|
|
13
|
+
const tree = await getMapArgument(source, "sync");
|
|
14
|
+
return mapReduce(tree, null, (values, keys) => {
|
|
15
|
+
const entries = [];
|
|
16
|
+
for (let i = 0; i < keys.length; i++) {
|
|
17
|
+
entries.push([keys[i], values[i]]);
|
|
18
|
+
}
|
|
19
|
+
return new SyncMap(entries);
|
|
20
|
+
});
|
|
21
|
+
}
|
package/src/operations/take.js
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* Returns a new
|
|
5
|
+
* Returns a new map with the number of keys limited to the indicated count.
|
|
5
6
|
*
|
|
6
|
-
* @param {import("../../index.ts").
|
|
7
|
+
* @param {import("../../index.ts").Maplike} maplike
|
|
7
8
|
* @param {number} count
|
|
8
9
|
*/
|
|
9
|
-
export default async function take(
|
|
10
|
-
const
|
|
10
|
+
export default async function take(maplike, count) {
|
|
11
|
+
const source = await getMapArgument(maplike, "take");
|
|
12
|
+
return Object.assign(new AsyncMap(), {
|
|
13
|
+
description: `take ${count}`,
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
15
|
+
async *keys() {
|
|
16
|
+
let i = 0;
|
|
17
|
+
for await (const key of source.keys()) {
|
|
18
|
+
yield key;
|
|
19
|
+
i += 1;
|
|
20
|
+
if (i >= count) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
16
24
|
},
|
|
17
25
|
|
|
18
26
|
async get(key) {
|
|
19
|
-
return
|
|
27
|
+
return source.get(key);
|
|
20
28
|
},
|
|
21
|
-
|
|
29
|
+
|
|
30
|
+
source: source,
|
|
31
|
+
|
|
32
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
33
|
+
});
|
|
22
34
|
}
|