object-hierarchy-access
Version:
Get/Set value from/to JS object hierarchy properties
586 lines (568 loc) • 18.2 kB
JavaScript
function normalizeDescriptor(info) {
if (typeof info === 'object' && info !== null) {
return info;
}
else if (typeof info === 'function') {
return {
getName: info,
value: {}
};
}
else {
return {
name: info,
value: {}
};
}
}
function isArray(source) {
return Array.isArray(source) || source instanceof Array;
}
function isObject(source) {
return typeof source === 'object' && source !== null;
}
function getOwnEnumerablePropKeys(target) {
const keys = Object.keys(target);
if (Object.getOwnPropertySymbols) {
const symbols = Object.getOwnPropertySymbols(target).filter(symbol => {
const descriptor = Object.getOwnPropertyDescriptor(target, symbol);
return descriptor && descriptor.enumerable;
});
if (symbols.length) {
keys.push(...symbols);
}
}
return keys;
}
function cloneContainer(from) {
if (isArray(from)) {
return [];
}
else if (isObject(from)) {
return {};
}
else {
return from;
}
}
function getPropName(current, descriptor) {
const { name, getName } = descriptor;
if (name !== undefined) {
return name;
}
if (getName) {
return getName.call(current, current);
}
}
function getNonEmptyPropName(current, descriptor) {
const name = getPropName(current, descriptor);
return name !== undefined ? name : 'undefined';
}
function getPropNames(current, descriptor) {
const { names, getNames } = descriptor;
if (names !== undefined) {
return isArray(names) ? names : [names];
}
if (getNames) {
const gotNames = getNames.call(current, current);
if (gotNames !== undefined) {
return isArray(gotNames) ? gotNames : [gotNames];
}
}
return getOwnEnumerablePropKeys(current);
}
const propProto = '__proto__';
function generate(target, hierarchies, forceOverride) {
let current = target;
hierarchies.forEach(info => {
const descriptor = normalizeDescriptor(info);
const { value, type, create, override, created, skipped, got } = descriptor;
const name = getNonEmptyPropName(current, descriptor);
if (forceOverride ||
override ||
!current[name] ||
typeof current[name] !== 'object' ||
(name === propProto && current[name] === Object.prototype)) {
const obj = value ? value :
type ? new type() :
create ? create.call(current, current, name) :
{};
current[name] = obj;
if (created) {
created.call(current, current, name, obj);
}
}
else {
if (skipped) {
skipped.call(current, current, name, current[name]);
}
}
const parent = current;
current = current[name];
if (got) {
got.call(parent, parent, name, current);
}
});
return current;
}
function setupIfUndef(target, hierarchies) {
return generate(target, hierarchies);
}
function setup(target, hierarchies) {
const current = generate(target, hierarchies.slice(0, -1));
const last = generate(current, hierarchies.slice(-1), true);
return { current, last };
}
function _parseArgs(others) {
const value = others[others.length - 1];
const rest = Array.prototype.concat.apply([], others.slice(0, -1)); // exclude `value`
const hierarchies = rest.slice(0, -1);
const prop = rest[rest.length - 1];
return { hierarchies, prop, value };
}
function set(optionalTarget, ...others) {
const { hierarchies, prop, value } = _parseArgs(others);
const target = optionalTarget || {};
const current = setupIfUndef(target, hierarchies);
current[prop] = value;
return target;
}
function assign(target, ...others) {
const { hierarchies, prop, value } = _parseArgs(others);
const current = setupIfUndef(target, hierarchies);
current[prop] = value;
return current;
}
function put(target, ...others) {
const { hierarchies, prop, value } = _parseArgs(others);
const current = setupIfUndef(target, hierarchies);
current[prop] = value;
return value;
}
function setIfUndef(optionalTarget, ...others) {
const { hierarchies, prop, value } = _parseArgs(others);
const target = optionalTarget || {};
const current = setupIfUndef(target, hierarchies);
if (current[prop] === undefined) {
current[prop] = value;
}
return target;
}
function assignIfUndef(target, ...others) {
const { hierarchies, prop, value } = _parseArgs(others);
const current = setupIfUndef(target, hierarchies);
if (current[prop] === undefined) {
current[prop] = value;
}
return current;
}
function putIfUndef(target, ...others) {
const { hierarchies, prop, value } = _parseArgs(others);
const current = setupIfUndef(target, hierarchies);
if (current[prop] === undefined) {
current[prop] = value;
}
return current[prop];
}
function _normalizeHierarchies(hierarchies) {
const result = Array.prototype.concat.apply([], hierarchies);
return result;
}
function setProp(optionalTarget, ...hierarchies) {
const arrHierarchies = _normalizeHierarchies(hierarchies);
const target = optionalTarget || {};
setup(target, arrHierarchies);
return target;
}
function assignProp(target, ...hierarchies) {
const arrHierarchies = _normalizeHierarchies(hierarchies);
const { current } = setup(target, arrHierarchies);
return current;
}
function putProp(target, ...hierarchies) {
const arrHierarchies = _normalizeHierarchies(hierarchies);
const { last } = setup(target, arrHierarchies);
return last;
}
function setPropIfUndef(optionalTarget, ...hierarchies) {
const arrHierarchies = _normalizeHierarchies(hierarchies);
const target = optionalTarget || {};
setupIfUndef(target, arrHierarchies);
return target;
}
function assignPropIfUndef(target, ...hierarchies) {
const arrHierarchies = _normalizeHierarchies(hierarchies);
const current = setupIfUndef(target, arrHierarchies.slice(0, -1));
setupIfUndef(current, arrHierarchies.slice(-1));
return current;
}
function putPropIfUndef(target, ...hierarchies) {
const arrHierarchies = _normalizeHierarchies(hierarchies);
return setupIfUndef(target, arrHierarchies);
}
function normalizeDescriptor$1(info) {
if (typeof info === 'object') {
return info;
}
else if (typeof info === 'function') {
return {
getValue: info
};
}
else {
return {
name: info
};
}
}
function getNameValue(current, descriptor) {
const { getValue } = descriptor;
let name = getPropName(current, descriptor);
let value;
if (name !== undefined) {
value = current[name];
}
else {
name = 'undefined';
if (getValue) {
value = getValue.call(current, current);
}
}
const { got } = descriptor;
if (got) {
got.call(current, current, name, value);
}
return { name, value };
}
function get(target, ...rest) {
const hierarchies = Array.prototype.concat.apply([], rest);
let current = target;
if (current !== undefined && current !== null) {
hierarchies.every(info => {
const descriptor = normalizeDescriptor$1(info);
const { value } = getNameValue(current, descriptor);
current = value;
return current;
});
}
return current;
}
function exist(target, ...rest) {
if (target === undefined || target === null) {
return false;
}
const hierarchies = Array.prototype.concat.apply([], rest);
let current = target;
for (let i = 0; i < hierarchies.length; i++) {
const prop = hierarchies[i];
if (!current || !(prop in current)) {
return false;
}
current = current[prop];
}
return true;
}
function _parseArgs$1(others) {
const callback = others[others.length - 1];
const hierarchies = Array.prototype.concat.apply([], others.slice(0, -1)); // exclude `callback`
return { hierarchies, callback };
}
function traverse(target, ...others) {
const { hierarchies, callback } = _parseArgs$1(others);
let current = target;
if (current !== undefined && current !== null) {
hierarchies.every(info => {
const descriptor = normalizeDescriptor$1(info);
const { name, value } = getNameValue(current, descriptor);
const parent = current;
current = value;
const result = callback.call(parent, parent, name, current);
return result !== false;
});
}
}
function traverseReverse(target, ...others) {
const { hierarchies, callback } = _parseArgs$1(others);
let current = target;
if (current !== undefined && current !== null) {
const params = [];
hierarchies.every(info => {
const descriptor = normalizeDescriptor$1(info);
const { name, value } = getNameValue(current, descriptor);
const parent = current;
current = value;
params.push({ parent, name, current });
return current;
});
for (let i = params.length - 1; i >= 0; i--) {
const item = params[i];
const result = callback.call(item.parent, item.parent, item.name, item.current);
if (result === false) {
break;
}
}
}
}
function array2map(arr, key, value) {
if (!isArray(arr)) {
return;
}
const result = {};
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
const keyProp = get(item, key);
const valueProp = get(item, value);
result[keyProp] = valueProp;
}
return result;
}
function map2array(obj, keyName, valueName) {
if (!obj) {
return;
}
const result = [];
getOwnEnumerablePropKeys(obj).forEach(key => {
const value = obj[key];
const keyProp = typeof keyName === 'function' ? keyName.call(obj, obj, key, value) : keyName;
const valueProp = typeof valueName === 'function' ? valueName.call(obj, obj, key, value) : valueName;
result.push({
[keyProp]: key,
[valueProp]: value
});
});
return result;
}
function normalizeDescriptor$2(info) {
if (isArray(info)) {
return {
names: info
};
}
else if (typeof info === 'object' && info !== null) {
return info;
}
else if (typeof info === 'function') {
return {
getNames: info
};
}
else {
return {
names: info
};
}
}
function getMappedNameValue(current, name, descriptor) {
const { got, mapName, mapValue, mapped } = descriptor;
const next = current[name];
if (got) {
got.call(current, current, name, next);
}
const mappedName = mapName ? mapName.call(current, current, name, next) : name;
const mappedValue = mapValue ? mapValue.call(current, current, name, next) : next;
if (mapped) {
mapped.call(current, current, mappedName, mappedValue);
}
return { mappedName, mappedValue };
}
function generate$1(current, result, hierarchies, index) {
const descriptor = normalizeDescriptor$2(hierarchies[index]);
const names = getPropNames(current, descriptor);
const lastIndex = hierarchies.length - 1;
names.forEach(name => {
if (name in current) {
const { mappedName, mappedValue } = getMappedNameValue(current, name, descriptor);
if (index < lastIndex) {
result[mappedName] = cloneContainer(mappedValue);
}
else {
result[mappedName] = mappedValue;
}
if (index < lastIndex && typeof mappedValue === 'object' && mappedValue !== null) {
generate$1(mappedValue, result[mappedName], hierarchies, index + 1);
}
}
});
}
function select(target, ...hierarchyProps) {
let result;
const current = target;
if (current !== undefined && current !== null) {
result = cloneContainer(current);
generate$1(current, result, hierarchyProps, 0);
}
return result;
}
function find(current, result, hierarchies, index) {
const descriptor = normalizeDescriptor$2(hierarchies[index]);
const names = getPropNames(current, descriptor);
const lastIndex = hierarchies.length - 1;
names.forEach(name => {
if (name in current) {
const { mappedValue } = getMappedNameValue(current, name, descriptor);
if (index < lastIndex) {
find(mappedValue, result, hierarchies, index + 1);
}
else {
result.push(mappedValue);
}
}
});
}
function pick(target, ...hierarchyProps) {
const result = [];
const current = target;
if (current !== undefined && current !== null) {
find(current, result, hierarchyProps, 0);
}
return result;
}
function normalizeDescriptor$3(info) {
if (typeof info === 'object' && info !== null) {
return info;
}
else if (typeof info === 'function') {
return {
by: info
};
}
else {
return {};
}
}
function _createContainer(descriptor, target) {
const { type, create } = descriptor;
if (type) {
return new type();
}
else if (create) {
return create.call(target, target);
}
else {
return {};
}
}
function group(target, ...params) {
if (!params.length) {
return target;
}
const descriptors = Array.prototype.concat.apply([], params).map(normalizeDescriptor$3).filter(d => d.by);
if (!descriptors.length) {
return target;
}
const lastIndex = descriptors.length - 1;
const keys = getOwnEnumerablePropKeys(target);
let rootContainer;
keys.forEach(key => {
const child = target[key];
let prevContainer;
let prevName;
descriptors.forEach((descriptor, index) => {
const { by } = descriptor;
if (index === 0) {
if (!rootContainer) {
rootContainer = _createContainer(descriptor, target);
}
prevContainer = rootContainer;
}
else {
if (!prevContainer[prevName]) {
prevContainer[prevName] = _createContainer(descriptor, target);
}
prevContainer = prevContainer[prevName];
}
const groupName = by.call(target, target, key, child);
if (index !== lastIndex) {
prevName = groupName;
}
else {
if (!prevContainer[groupName]) {
prevContainer[groupName] = cloneContainer(target);
}
const currentContainer = prevContainer[groupName];
if (isArray(currentContainer)) {
currentContainer.push(child);
}
else {
currentContainer[key] = child;
}
}
});
});
return rootContainer;
}
function _getDimTypes(input, maxDim = 16) {
const types = [];
if (isObject(input)) {
let type = isArray(input) ? Array : Object;
let dimItems = [input];
for (let iDim = 0; iDim <= maxDim; iDim++) {
let nextType = Array;
let nextDimItems = [];
dimItems.forEach(dimItem => {
getOwnEnumerablePropKeys(dimItem).forEach(key => {
const nextDimItem = dimItem[key];
if (isObject(nextDimItem)) {
if (!isArray(nextDimItem)) {
nextType = Object;
}
nextDimItems.push(nextDimItem);
}
});
});
types.push(type);
if (!nextDimItems.length) {
break;
}
type = nextType;
dimItems = nextDimItems;
}
}
return types;
}
function redim(data, ...newDimsOrder) {
if (!data) {
return data;
}
// newDims: new order of old dims
const newDims = Array.prototype.concat.apply([], newDimsOrder);
if (!newDims.length) {
return data;
}
const oldDimMin = Math.min(...newDims);
if (oldDimMin < 0) {
return;
}
const oldDimMax = Math.max(...newDims);
const newDimMax = newDims.length - 1;
const dimTypes = _getDimTypes(data, oldDimMax);
if (!dimTypes.length || oldDimMax >= dimTypes.length) {
return;
}
const result = new dimTypes[newDims[0]];
const _walk = function _walk(path, current, currentDim) {
if (currentDim <= oldDimMax) {
getOwnEnumerablePropKeys(current).forEach(key => {
const nextDim = currentDim + 1;
if (exist(current, key)) {
_walk(path.concat(key), current[key], nextDim);
}
});
}
else {
const newHierarchyDescriptors = newDims.map(((oldDim, newDim) => {
return newDim < newDimMax ? {
name: path[oldDim],
type: dimTypes[newDims[newDim + 1]],
} : {
name: path[oldDim],
value: current
};
}));
setProp(result, newHierarchyDescriptors);
}
};
_walk([], data, 0);
return result;
}
export { array2map, assign, assignIfUndef, assignProp, assignPropIfUndef, exist, get, group, map2array, pick, put, putIfUndef, putProp, putPropIfUndef, redim, select, set, setIfUndef, setProp, setPropIfUndef, traverse, traverseReverse };