@weborigami/async-tree 0.7.0-beta.1 → 0.7.0-beta.2
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/package.json +1 -1
- package/src/Tree.js +2 -0
- package/src/drivers/AsyncMap.js +1 -0
- package/src/drivers/SyncMap.js +1 -0
- package/src/operations/find.js +21 -0
- package/src/operations/findKey.js +21 -0
- package/src/operations/from.js +18 -6
- package/src/operations/inflatePaths.js +7 -6
- package/test/operations/find.test.js +10 -0
- package/test/operations/findKey.test.js +15 -0
- package/test/operations/from.test.js +18 -0
package/package.json
CHANGED
package/src/Tree.js
CHANGED
|
@@ -25,6 +25,8 @@ export { default as deflatePaths } from "./operations/deflatePaths.js";
|
|
|
25
25
|
export { default as delete } from "./operations/delete.js";
|
|
26
26
|
export { default as entries } from "./operations/entries.js";
|
|
27
27
|
export { default as filter } from "./operations/filter.js";
|
|
28
|
+
export { default as find } from "./operations/find.js";
|
|
29
|
+
export { default as findKey } from "./operations/findKey.js";
|
|
28
30
|
export { default as first } from "./operations/first.js";
|
|
29
31
|
export { default as flat } from "./operations/flat.js";
|
|
30
32
|
export { default as forEach } from "./operations/forEach.js";
|
package/src/drivers/AsyncMap.js
CHANGED
package/src/drivers/SyncMap.js
CHANGED
|
@@ -49,6 +49,7 @@ export default class SyncMap extends Map {
|
|
|
49
49
|
if (!(result instanceof Map)) {
|
|
50
50
|
// Create new child node using no-arg constructor
|
|
51
51
|
result = new /** @type {any} */ (this.constructor)();
|
|
52
|
+
result.trailingSlashKeys = this.trailingSlashKeys;
|
|
52
53
|
this.set(key, result);
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as args from "../utilities/args.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Return the first value in the map that satisfies the given predicate
|
|
5
|
+
* function.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
|
+
*
|
|
9
|
+
* @param {Maplike} maplike
|
|
10
|
+
* @param {Function} predicate
|
|
11
|
+
*/
|
|
12
|
+
export default async function find(maplike, predicate) {
|
|
13
|
+
const map = await args.map(maplike, "Tree.find");
|
|
14
|
+
for await (const key of map.keys()) {
|
|
15
|
+
const value = await map.get(key);
|
|
16
|
+
if (await predicate(value, key, map)) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as args from "../utilities/args.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Return the first key in the map that satisfies the given predicate
|
|
5
|
+
* function.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
|
+
*
|
|
9
|
+
* @param {Maplike} maplike
|
|
10
|
+
* @param {Function} predicate
|
|
11
|
+
*/
|
|
12
|
+
export default async function findKey(maplike, predicate) {
|
|
13
|
+
const map = await args.map(maplike, "Tree.findKey");
|
|
14
|
+
for await (const key of map.keys()) {
|
|
15
|
+
const value = await map.get(key);
|
|
16
|
+
if (await predicate(value, key, map)) {
|
|
17
|
+
return key;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
package/src/operations/from.js
CHANGED
|
@@ -2,13 +2,16 @@ import AsyncMap from "../drivers/AsyncMap.js";
|
|
|
2
2
|
import FunctionMap from "../drivers/FunctionMap.js";
|
|
3
3
|
import ObjectMap from "../drivers/ObjectMap.js";
|
|
4
4
|
import SetMap from "../drivers/SetMap.js";
|
|
5
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
5
6
|
import * as symbols from "../symbols.js";
|
|
6
7
|
import box from "../utilities/box.js";
|
|
7
|
-
import isPlainObject from "../utilities/isPlainObject.js";
|
|
8
8
|
import setParent from "../utilities/setParent.js";
|
|
9
9
|
import TypedArray from "../utilities/TypedArray.js";
|
|
10
10
|
import isMap from "./isMap.js";
|
|
11
11
|
|
|
12
|
+
// Base class for async functions
|
|
13
|
+
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
16
|
* Attempts to cast the indicated object to a map.
|
|
14
17
|
*
|
|
@@ -42,15 +45,17 @@ export default function from(object, options = {}) {
|
|
|
42
45
|
map = new SetMap(object);
|
|
43
46
|
} else if (object instanceof ArrayBuffer || object instanceof TypedArray) {
|
|
44
47
|
throw new TypeError("Can't treat binary file data as a map.");
|
|
45
|
-
} else if (isPlainObject(object) || object instanceof Array) {
|
|
46
|
-
map = new ObjectMap(object, { deep });
|
|
47
48
|
// @ts-ignore
|
|
48
49
|
} else if (globalThis.Iterator && object instanceof Iterator) {
|
|
49
50
|
const array = Array.from(object);
|
|
50
51
|
map = new ObjectMap(array, { deep });
|
|
51
|
-
} else if (
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
} else if (typeof object === "object") {
|
|
53
|
+
if (typeof object.get === "function" && typeof object.keys === "function") {
|
|
54
|
+
map = upgradeToMap(object);
|
|
55
|
+
} else {
|
|
56
|
+
// A plain object, array, or instance of some class
|
|
57
|
+
map = new ObjectMap(object, { deep });
|
|
58
|
+
}
|
|
54
59
|
} else if (
|
|
55
60
|
typeof object === "string" ||
|
|
56
61
|
typeof object === "number" ||
|
|
@@ -69,3 +74,10 @@ export default function from(object, options = {}) {
|
|
|
69
74
|
|
|
70
75
|
return map;
|
|
71
76
|
}
|
|
77
|
+
|
|
78
|
+
function upgradeToMap(object) {
|
|
79
|
+
const isAsync =
|
|
80
|
+
object.get instanceof AsyncFunction || object.keys instanceof AsyncFunction;
|
|
81
|
+
const baseClass = isAsync ? AsyncMap : SyncMap;
|
|
82
|
+
return Object.assign(new baseClass(), object);
|
|
83
|
+
}
|
|
@@ -5,23 +5,24 @@ import keysFromPath from "../utilities/keysFromPath.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Given a mapping of string paths to values, return the described tree.
|
|
7
7
|
*/
|
|
8
|
-
export default async function inflatePaths(maplike) {
|
|
9
|
-
const map = await args.map(maplike, "Tree.inflatePaths"
|
|
8
|
+
export default async function inflatePaths(maplike, options = {}) {
|
|
9
|
+
const map = await args.map(maplike, "Tree.inflatePaths");
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const classFn = options.classFn ?? SyncMap;
|
|
12
|
+
const result = new classFn();
|
|
12
13
|
for await (const [path, value] of map) {
|
|
13
14
|
const keys = keysFromPath(path);
|
|
14
|
-
setValue(result, keys, value);
|
|
15
|
+
setValue(result, keys, value, classFn);
|
|
15
16
|
}
|
|
16
17
|
return result;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// Add the value to the tree at the given path of keys
|
|
20
|
-
function setValue(map, keys, value) {
|
|
21
|
+
function setValue(map, keys, value, classFn) {
|
|
21
22
|
let node = map;
|
|
22
23
|
for (const key of keys.slice(0, -1)) {
|
|
23
24
|
// Create a new node if one doesn't exist yet
|
|
24
|
-
node = node.getOrInsertComputed(key, () => new
|
|
25
|
+
node = node.getOrInsertComputed(key, () => new classFn());
|
|
25
26
|
}
|
|
26
27
|
node.set(keys[keys.length - 1], value);
|
|
27
28
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import find from "../../src/operations/find.js";
|
|
4
|
+
|
|
5
|
+
describe("find", () => {
|
|
6
|
+
test("returns the first value that satisfies the predicate", async () => {
|
|
7
|
+
const result = await find([5, 12, 8], (e) => e > 10);
|
|
8
|
+
assert.strictEqual(result, 12);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import findKey from "../../src/operations/findKey.js";
|
|
4
|
+
|
|
5
|
+
describe("findKey", () => {
|
|
6
|
+
test("returns the first key that satisfies the predicate", async () => {
|
|
7
|
+
const object = {
|
|
8
|
+
a: 5,
|
|
9
|
+
b: 12,
|
|
10
|
+
c: 8,
|
|
11
|
+
};
|
|
12
|
+
const result = await findKey(object, (e) => e > 10);
|
|
13
|
+
assert.strictEqual(result, "b");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -58,4 +58,22 @@ describe("from", () => {
|
|
|
58
58
|
const result = await slice(0, 5);
|
|
59
59
|
assert.equal(result, "Hello");
|
|
60
60
|
});
|
|
61
|
+
|
|
62
|
+
test("upgrades object with get/keys to a Map", () => {
|
|
63
|
+
const object = {
|
|
64
|
+
get(key) {
|
|
65
|
+
return key.toUpperCase();
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
*keys() {
|
|
69
|
+
yield "a";
|
|
70
|
+
yield "b";
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
const tree = from(object);
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
assert.deepEqual([...tree.keys()], ["a", "b"]);
|
|
76
|
+
assert.equal(tree.get("a"), "A");
|
|
77
|
+
assert.equal(tree.get("b"), "B");
|
|
78
|
+
});
|
|
61
79
|
});
|