@dot-event/store
Version:
dot-event store
175 lines (152 loc) • 3.93 kB
JavaScript
/* eslint-disable max-len, sort-keys */
/*prettier-ignore*/
;
/**
* Set a value by a dot path.
* @param obj The object to evaluate.
* @param prop The path to be set.
* @param val The value to set.
*/
function set(obj, prop, value) {
prop = propToArray(prop)
var setPropImmutableRec = function(obj, prop, value, i) {
var clone,
head = prop[i]
if (prop.length > i) {
if (Array.isArray(obj)) {
head = getArrayIndex(head, obj)
clone = obj.slice()
} else {
clone = Object.assign({}, obj)
}
clone[head] = setPropImmutableRec(
obj[head] !== undefined ? obj[head] : {},
prop,
value,
i + 1
)
return clone
}
return typeof value === "function" ? value(obj) : value
}
return setPropImmutableRec(obj, prop, value, 0)
}
/**
* Get a value by a dot path.
* @param obj The object to evaluate.
* @param prop The path to value that should be returned.
*/
function get(obj, prop, value) {
prop = propToArray(prop)
for (var i = 0; i < prop.length; i++) {
if (typeof obj !== "object") {
return value
}
var head = prop[i]
if (Array.isArray(obj) && head === "$end") {
head = obj.length - 1
}
obj = obj[head]
}
if (typeof obj === "undefined") {
return value
}
return obj
}
/**
* Delete a property by a dot path.
* If target container is an object, the property is deleted.
* If target container is an array, the index is deleted.
* If target container is undefined, nothing is deleted.
* @param obj The object to evaluate.
* @param prop The path to the property or index that should be deleted.
*/
function _delete(obj, prop) {
prop = propToArray(prop)
var deletePropImmutableRec = function(obj, prop, i) {
var clone,
head = prop[i]
if (
typeof obj !== "object" ||
(!Array.isArray(obj) && obj[head] === undefined)
) {
return obj
}
if (prop.length - 1 > i) {
if (Array.isArray(obj)) {
head = getArrayIndex(head, obj)
clone = obj.slice()
} else {
clone = Object.assign({}, obj)
}
clone[head] = deletePropImmutableRec(
obj[head],
prop,
i + 1
)
return clone
}
if (Array.isArray(obj)) {
head = getArrayIndex(head, obj)
clone = [].concat(
obj.slice(0, head),
obj.slice(head + 1)
)
} else {
clone = Object.assign({}, obj)
delete clone[head]
}
return clone
}
return deletePropImmutableRec(obj, prop, 0)
}
/**
* Merges a value. The target value must be an object, array, null, or undefined.
* If target is an object, Object.assign({}, target, param) is used.
* If target an array, target.concat(param) is used.
* If target is null or undefined, the value is simply set.
* @param obj The object to evaluate.
* @param prop The path to the value.
* @param val The value to merge into the target value.
*/
function merge(obj, prop, val) {
var curVal = get(obj, prop)
if (typeof curVal === "object") {
if (Array.isArray(curVal)) {
return set(obj, prop, curVal.concat(val))
} else if (curVal === null) {
return set(obj, prop, val)
} else {
var merged = Object.assign({}, curVal, val)
return set(obj, prop, merged)
}
} else if (typeof curVal === "undefined") {
return set(obj, prop, val)
} else {
return obj
}
}
function getArrayIndex(head, obj) {
if (head === "$end") {
head = Math.max(obj.length - 1, 0)
}
if (!/^\+?\d+$/.test(head)) {
throw new Error(
"Array index '" + head + "' has to be an integer"
)
}
return parseInt(head)
}
function propToArray(prop) {
if (Array.isArray(prop)) {
return prop
}
return prop.split(".")
}
module.exports = {
delete: _delete,
get: get,
merge: merge,
propToArray: propToArray,
set: set,
}