Skip to content

Instantly share code, notes, and snippets.

@nanxiaobei

nanxiaobei/chain.js

Last active Feb 25, 2020
Embed
What would you like to do?
Inspired by optional chaining
const isObj = (val) => Object.prototype.toString.call(val) === '[object Object]';
const isArr = (val) => Array.isArray(val);
const isNum = (val) => val === `${+val}`;
const strToNum = (str) => (isNum(str) ? +str : str);
/**
* Used to change undefined keys to object or array, similar effect to the 'optional chaining'.
* Received an object and an array of keys, returns a formatted object.
*
* e.g.
* list -> ['a', 'b.c'] -> a is object, b is object, b.c is object
* list -> ['a[]', 'b.c[]'] -> a is array
* list -> ['b[0].c'] -> b is array, b[0] is object, b[0].c is object
* list -> ['b[0][]'] -> b is array, b[0] is array
*
* Default, change last key to object
* chain({ a: 1 }, ['a', 'b.c', 'd']) -> { a: {}, b: { c: {} }, d: {} } // change not object to object, add missed object
* chain({ a: { b: 1 } }, ['a', 'd']) -> { a: { b: 1 } }, d: {} } // if object exists, not change
*
* chain({ a: 1 }, ['b[0]']) -> { a: 1, b: [{}] } // b -> array, b[0] -> object
* chain({ a: 1 }, ['b[0].c']) -> { a: 1, b: [{ c: {} }] } // b -> array, b[0] -> object, b[0].c -> object
*
* To change last key to array, not object, add '[]' behind.
* chain({ a: 1 }, ['b[]']) -> { a: 1, b: [] } // b -> array
* chain({ a: 1 }, ['b[0][]']) -> { a: 1, b: [[]] } // b -> array, b[0] -> array
*
* @param {object} obj - source object
* @param {array} list - needed keys for source object
* @returns {object} formatted object
*/
const chain = (obj, list) => {
list.forEach((key) => {
// e.g.
// key: prop[0][0] -> chain({}, [key]) -> { prop: [ [ {} ] ] }
// key: prop[0][] -> chain({}, [key]) -> { prop: [ [] ] }
// key: prop -> chain({}, [key]) -> { prop: {} }
const newKey = key.replace(/\[(\d+)]/g, '[].$1');
// prop[0][0] -> prop[].0[].0 (key -> newKey)
// prop[0][] -> prop[].0[] (key -> newKey)
// prop -> prop (key -> newKey)
const keyPieces = newKey.split('.');
// prop[].0[].0 -> ['prop[]', '0[]', '0'] (newKey -> keyPieces)
// prop[].0[] -> ['prop[]', '0[]'] (newKey -> keyPieces)
// prop -> ['prop'] (newKey -> keyPieces)
keyPieces.reduce((data, oldPiece) => {
let newPiece = oldPiece;
if (newPiece.includes('[]')) {
// newPiece: 'prop[]' or '0[]' (data[newPiece] is array)
newPiece = newPiece.slice(0, -2); // 'prop[]' -> 'prop', '0[]' -> '0'
newPiece = strToNum(newPiece); // 'prop' -> 'prop', '0' -> 0
if (!isArr(data[newPiece])) {
data[newPiece] = [];
}
} else {
// newPiece: 'prop' or '0' (data[newPiece] is object)
newPiece = strToNum(newPiece); // 'prop' -> 'prop', '0' -> 0
if (!isObj(data[newPiece])) {
data[newPiece] = {};
}
}
return data[newPiece];
}, obj);
});
return obj;
};
export default chain;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment