Select Git revision
buildtree.mjs

Volker Schukai authored
buildtree.mjs 2.78 KiB
/**
* Copyright schukai GmbH and contributors 2022. 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;
}