a-to-treejs
Version:
Convert array to tree fastly, and opposite.
562 lines (459 loc) • 15.7 kB
JavaScript
import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray';
import _createForOfIteratorHelper from '@babel/runtime/helpers/esm/createForOfIteratorHelper';
import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
import kindOf from 'kind-of';
import isPlainObject from 'is-plain-obj';
/** whether v is among (null, undefined, '') */
function isNothing(v) {
return v === null || v === undefined || v === '';
}
/** The first-level element in result as the topest branch node */
function convert1$1(data, adapter) {
var result = [];
var nodeMap = new Map();
var _adapter$id = adapter.id,
id = _adapter$id === void 0 ? 'id' : _adapter$id,
_adapter$parent_id = adapter.parent_id,
parent_id = _adapter$parent_id === void 0 ? 'parent_id' : _adapter$parent_id,
_adapter$children_pro = adapter.children_prop,
children_prop = _adapter$children_pro === void 0 ? 'children' : _adapter$children_pro,
parent_prop = adapter.parent_prop,
container_prop = adapter.container_prop;
var createNode = function () {
if (container_prop) {
if (parent_prop) {
return function (value) {
var node = {};
node[parent_prop] = null;
node[children_prop] = [];
node[container_prop] = value;
return node;
};
} else {
return function (value) {
var node = {};
node[container_prop] = value;
node[children_prop] = [];
return node;
};
}
} else {
if (parent_prop) {
return function (value) {
var node = value;
node[children_prop] = [];
node[parent_prop] = null;
return node;
};
} else {
return function (value) {
value[children_prop] = [];
return value;
};
}
}
}();
for (var i = 0, node, nodeId, nodePId, shouleHasParent, vnodeSelf, parent; i < data.length; i++) {
node = createNode(data[i]);
nodeId = data[i][id];
nodePId = data[i][parent_id];
vnodeSelf = nodeMap.get(nodeId);
parent = nodeMap.get(nodePId);
/** the node as the topest branch node. */
shouleHasParent = !isNothing(nodePId);
if (!parent && shouleHasParent) {
var vnode = createNode({});
nodeMap.set(nodePId, vnode);
parent = vnode;
}
if (shouleHasParent && parent_prop) {
node[parent_prop] = parent;
}
var realNode = vnodeSelf || node;
if (vnodeSelf) {
// merge node
var c = vnodeSelf[children_prop];
Object.assign(vnodeSelf, node);
vnodeSelf[children_prop] = c;
} else {
nodeMap.set(nodeId, node);
}
if (shouleHasParent) {
parent[children_prop].push(realNode);
} else {
result.push(realNode);
}
}
nodeMap.clear();
return result;
}
/** The first-level element in result as leaf node */
function convert2$1(data, adapter) {
var nodeMap = new Map();
var isBranchMap = new Map();
var _adapter$id = adapter.id,
id = _adapter$id === void 0 ? 'id' : _adapter$id,
_adapter$parent_id = adapter.parent_id,
parent_id = _adapter$parent_id === void 0 ? 'parent_id' : _adapter$parent_id,
_adapter$parent_prop = adapter.parent_prop,
parent_prop = _adapter$parent_prop === void 0 ? 'parent' : _adapter$parent_prop,
children_prop = adapter.children_prop,
container_prop = adapter.container_prop;
var createNode = function () {
if (container_prop) {
if (children_prop) {
return function (value) {
var node = {};
node[parent_prop] = null;
node[children_prop] = [];
node[container_prop] = value;
return node;
};
} else {
return function (value) {
var node = {};
node[parent_prop] = null;
node[container_prop] = value;
return node;
};
}
}
if (children_prop) {
return function (value) {
value[parent_prop] = null;
value[children_prop] = [];
return value;
};
} else {
return function (value) {
value[parent_prop] = null;
return value;
};
}
}();
for (var i = 0, node, nodeId, nodePId, vnodeSelf, parent, shouleHasParent; i < data.length; i++) {
node = createNode(data[i]);
nodeId = data[i][id];
nodePId = data[i][parent_id];
shouleHasParent = !isNothing(nodePId);
isBranchMap.set(nodeId, false);
shouleHasParent && isBranchMap.set(nodePId, true);
parent = nodeMap.get(nodePId) || null;
if (!parent && shouleHasParent) {
var vnode = createNode({});
nodeMap.set(nodePId, vnode);
parent = vnode;
}
node[parent_prop] = parent;
vnodeSelf = nodeMap.get(nodeId);
if (vnodeSelf) {
var children = children_prop ? vnodeSelf[children_prop] : null;
Object.assign(vnodeSelf, node);
children && (vnodeSelf[children_prop] = children);
children_prop && parent && parent[children_prop].push(vnodeSelf);
} else {
nodeMap.set(nodeId, node);
children_prop && parent && parent[children_prop].push(node);
}
}
var leafNodeIds = [];
var _iterator = _createForOfIteratorHelper(isBranchMap.entries()),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _slicedToArray(_step.value, 2),
_id = _step$value[0],
flag = _step$value[1];
if (!flag && _id) leafNodeIds.push(_id);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var result = leafNodeIds.map(function (id) {
return nodeMap.get(id);
});
nodeMap.clear();
return result;
}
var _excluded$1 = ["strict"];
function arrayToTree$1(data) {
var adapter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (kindOf(data) !== 'array') {
return [];
}
var errors = validateAdapter$1(adapter);
if (errors.length > 0) {
var msg = "The 'adapter' is invalid, causes: \n ".concat(errors.join('\n'));
throw new Error(msg);
}
var _adapter$strict = adapter.strict,
strict = _adapter$strict === void 0 ? false : _adapter$strict,
rest = _objectWithoutProperties(adapter, _excluded$1);
if (strict) {
var _catchErrors = catchErrors(data, {
id: adapter.id,
parent_id: adapter.parent_id
}),
detail = _catchErrors.detail,
errorKinds = _catchErrors.errorKinds;
if (detail.length > 0) {
console.log('Inregular data causes: \n', detail);
throw new Error('The data isn\'t regular, causes: ' + errorKinds.join(', '));
}
}
var _adapter$root = adapter.root,
root = _adapter$root === void 0 ? 'branch' : _adapter$root;
return root === 'branch' ? convert1$1(data, rest) : convert2$1(data, rest);
}
function validateAdapter$1(adapter) {
var errors = [];
if (!isPlainObject(adapter)) {
errors.push("Expected 'adapter' as a plain object, instead of ".concat(kindOf(adapter), " "));
}
var root = adapter.root,
id = adapter.id,
parent_id = adapter.parent_id,
parent_prop = adapter.parent_prop,
children_prop = adapter.children_prop,
container_prop = adapter.container_prop;
if (root && root !== 'branch' && root !== 'leaf') {
errors.push("Expected 'adapter.root' must be 'branch' or 'leaf', instead of ".concat(root));
}
if (id && parent_id && id === parent_id) {
errors.push('The \'adapter.id\' equals with \'adapter.parent_id\'.');
}
var k1 = kindOf(id);
if (id && k1 !== 'string') {
errors.push("Expected 'adapter.id' as a not empty string, instead of ".concat(id, "."));
}
var k2 = kindOf(parent_id);
if (parent_id && k2 !== 'string') {
errors.push("Expected 'adapter.parent_id' as a not empty string, instead of ".concat(parent_id, "."));
}
var k3 = kindOf(parent_prop);
if (parent_prop && k3 !== 'string') {
errors.push("Expected 'adapter.parent_prop' as a not empty string, instead of ".concat(parent_prop, "."));
}
var k4 = kindOf(children_prop);
if (children_prop && k4 !== 'string') {
errors.push("Expected 'adapter.children_prop' as a not empty string, instead of ".concat(children_prop, "."));
}
var k5 = kindOf(container_prop);
if (container_prop && k5 !== 'string') {
errors.push("Expected 'adapter.container_prop' as a not empty string, instead of ".concat(container_prop, "."));
}
return errors;
}
function catchErrors(data) {
var adapter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var DUPLICATED_ID_ERROR = 'duplicated id';
var UNKNOWN_PARENT_ID_ERROR = 'unknown parent_id';
var INFINITE_REF_ERROR = 'self.id equals oneself ancestor.id';
var nodeMap = new Map();
var errorLogMap = new Map();
var parentIdMap = new Map();
var _adapter$id = adapter.id,
idKey = _adapter$id === void 0 ? 'id' : _adapter$id,
_adapter$parent_id = adapter.parent_id,
parentIdKey = _adapter$parent_id === void 0 ? 'parent_id' : _adapter$parent_id;
var errorKindMap = new Map();
errorKindMap.set(DUPLICATED_ID_ERROR, false);
errorKindMap.set(UNKNOWN_PARENT_ID_ERROR, false);
errorKindMap.set(INFINITE_REF_ERROR, false);
function addError(node, error) {
errorKindMap.set(error, true);
var id = node[idKey];
if (errorLogMap.has(id)) {
errorLogMap.get(id).errors.push(error);
} else {
errorLogMap.set(id, {
node: node,
errors: [error]
});
}
}
for (var i = 0, el, id, parent_id; i < data.length; i++) {
el = data[i];
id = el[idKey];
parent_id = el[parentIdKey];
if (nodeMap.has(id)) {
addError(el, DUPLICATED_ID_ERROR);
} else {
nodeMap.set(id, el);
parentIdMap.set(id, parent_id);
}
}
var _iterator = _createForOfIteratorHelper(parentIdMap.entries()),
_step;
try {
var _loop = function _loop() {
var _step$value = _slicedToArray(_step.value, 2),
selfId = _step$value[0],
parentId = _step$value[1];
if (!isNothing(parentId)) {
var recur = function recur(ancestorId) {
if (isNothing(ancestorId)) return;
if (selfId === ancestorId) {
addError(nodeMap.get(selfId), INFINITE_REF_ERROR);
} else {
recur(parentIdMap.get(ancestorId));
}
};
if (!nodeMap.has(parentId)) {
addError(nodeMap.get(selfId), UNKNOWN_PARENT_ID_ERROR);
}
recur(parentId);
}
};
for (_iterator.s(); !(_step = _iterator.n()).done;) {
_loop();
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var errors = [];
var _iterator2 = _createForOfIteratorHelper(errorLogMap.entries()),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var _step2$value = _slicedToArray(_step2.value, 2),
error = _step2$value[1];
errors.push(error);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
var errorKinds = [];
var _iterator3 = _createForOfIteratorHelper(errorKindMap.entries()),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var _step3$value = _slicedToArray(_step3.value, 2),
kind = _step3$value[0],
flag = _step3$value[1];
if (flag) errorKinds.push(kind);
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
nodeMap.clear();
errorLogMap.clear();
parentIdMap.clear();
errorKindMap.clear();
return {
detail: errors,
errorKinds: errorKinds
};
}
/** Flatten nodes, the first-element as the topest branch node. */
function convert1(data, adapter) {
var parent_prop = adapter.parent_prop,
_adapter$children_pro = adapter.children_prop,
children_prop = _adapter$children_pro === void 0 ? 'children' : _adapter$children_pro,
container_prop = adapter.container_prop;
var result = [];
function recur(nodes) {
for (var i = 0, node, nodeValue, children; i < nodes.length; i++) {
node = nodes[i];
nodeValue = container_prop ? node[container_prop] : node;
children = node[children_prop] || [];
if (!container_prop) {
delete nodeValue[children_prop];
parent_prop && delete nodeValue[parent_prop];
}
result.push(nodeValue);
recur(children);
}
}
recur(data);
return result;
}
/** Flatten nodes, the first-element as leaf node. */
function convert2(data, adapter) {
var _adapter$id = adapter.id,
id = _adapter$id === void 0 ? 'id' : _adapter$id,
_adapter$parent_prop = adapter.parent_prop,
parent_prop = _adapter$parent_prop === void 0 ? 'parent' : _adapter$parent_prop,
children_prop = adapter.children_prop,
container_prop = adapter.container_prop;
var result = [];
var collected = new Map();
function recur(node) {
var nodeValue = container_prop ? node[container_prop] : node;
var nodeId = nodeValue[id];
if (collected.has(nodeId)) return;
var parent = node[parent_prop];
if (!container_prop) {
delete nodeValue[parent_prop];
children_prop && delete nodeValue[children_prop];
}
collected.set(nodeId, 1);
result.push(nodeValue);
parent && recur(parent);
}
for (var i = 0; i < data.length; i++) {
recur(data[i]);
}
collected.clear();
return result;
}
var _excluded = ["root"];
function treeToArray$1(data) {
var adapter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (kindOf(data) !== 'array') {
return [];
}
var errors = validateAdapter(adapter);
if (errors.length > 0) {
var msg = "The 'adapter' is invalid, causes: \n ".concat(errors.join('\n'));
throw new Error(msg);
}
var _adapter$root = adapter.root,
root = _adapter$root === void 0 ? 'branch' : _adapter$root,
rest = _objectWithoutProperties(adapter, _excluded);
return root === 'branch' ? convert1(data, rest) : convert2(data, rest);
}
function validateAdapter() {
var adapter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var errors = [];
if (!isPlainObject(adapter)) {
errors.push("Expected 'adapter' as a plain object, instead of ".concat(kindOf(adapter), " "));
}
var id = adapter.id,
_adapter$root2 = adapter.root,
root = _adapter$root2 === void 0 ? 'branch' : _adapter$root2,
children_prop = adapter.children_prop,
parent_prop = adapter.parent_prop,
container_prop = adapter.container_prop;
if (root && root !== 'branch' && root !== 'leaf') {
errors.push("Expected 'adapter.root' must be 'branch' or 'leaf', instead of ".concat(root));
}
var k1 = kindOf(id);
if (!id || k1 !== 'string') {
errors.push("Expected 'adapter.id' as a not empty string, instead of ".concat(id, "."));
}
var k2 = kindOf(parent_prop);
if (parent_prop && k2 !== 'string') {
errors.push("Expected 'adapter.parent_prop' as a not empty string, instead of ".concat(parent_prop, "."));
}
var k3 = kindOf(children_prop);
if (children_prop && k3 !== 'string') {
errors.push("Expected 'adapter.children_prop' as a not empty string, instead of ".concat(children_prop, "."));
}
var k4 = kindOf(container_prop);
if (container_prop && k4 !== 'string') {
errors.push("Expected 'adapter.container_prop' as a not empty string, instead of ".concat(container_prop, "."));
}
return errors;
}
var treeToArray = treeToArray$1;
var arrayToTree = arrayToTree$1;
export { arrayToTree as default, treeToArray };