Something went wrong on our end
Select Git revision
-
Volker Schukai authoredVolker Schukai authored
buildtree.mjs 2.84 KiB
/**
* Copyright schukai GmbH and contributors 2023. All Rights Reserved.
* Node module: @schukai/monster
* This file is licensed under the AGPLv3 License.
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
*/
import { isArray, isObject } from "../types/is.mjs";
import { Node } from "../types/node.mjs";
import { NodeList } from "../types/nodelist.mjs";
import { assembleParts } from "./buildmap.mjs";
import { extend } from "./extend.mjs";
export { buildTree };
/**
* @private
* @type {symbol}
*/
const parentSymbol = Symbol("parent");
/**
* @private
* @type {symbol}
*/
const rootSymbol = Symbol("root");
/**
* @typedef {Object} buildTreeOptions
* @property {array} options.rootReferences=[null, undefined] defines the values for elements without parents
* @property {Monster.Data~exampleFilterCallback} options.filter filtering of the values
* @memberOf Monster.Data
*/
/**
* With the help of the function `buildTree()`, nodes can be easily created from data objects.
*
* @param {*} subject
* @param {string|Monster.Data~exampleSelectorCallback} selector
* @param {string} idKey
* @param {string} parentIDKey
* @param {buildTreeOptions} [options]
* @return {*}
* @memberOf Monster.Data
* @throws {TypeError} value is neither a string nor a function
* @throws {TypeError} the selector callback must return a map
* @throws {Error} the object has no value for the specified id
* @license AGPLv3
* @since 1.26.0
*/
function buildTree(subject, selector, idKey, parentIDKey, options) {
const nodes = new Map();
if (!isObject(options)) {
options = {};
}
options = extend(
{},
{
rootReferences: [null, undefined],
filter: undefined,
},
options,
);
const filter = options?.filter;
let rootReferences = options.rootReferences;
if (!isArray(rootReferences)) {
rootReferences = [rootReferences];
}
const childMap = assembleParts(subject, selector, filter, function (o, k, m) {
const key = o?.[idKey];
let ref = o?.[parentIDKey];
if (rootReferences.indexOf(ref) !== -1) ref = rootSymbol;
if (key === undefined) {
throw new Error("the object has no value for the specified id");
}
o[parentSymbol] = ref;
const node = new Node(o);
this.has(ref) ? this.get(ref).add(node) : this.set(ref, new NodeList().add(node));
nodes.set(key, node);
});
nodes.forEach((node) => {
let id = node?.["value"]?.[idKey];
if (childMap.has(id)) {
node.childNodes = childMap.get(id);
childMap.delete(id);
}
});
const list = new NodeList();
childMap.forEach((s) => {
if (s instanceof Set) {
s.forEach((n) => {
list.add(n);
});
}
});
return list;
}