updeep-remeda/lib/update.js

105 lines
2.3 KiB
JavaScript
Raw Normal View History

2017-04-19 00:55:46 +00:00
import isPlainObject from 'lodash/isPlainObject'
2018-11-28 00:56:13 +00:00
import _omitBy from 'lodash/omitBy'
2017-04-19 00:55:46 +00:00
import wrap from './wrap'
2018-11-28 00:56:13 +00:00
import constant from './constant'
const innerOmitted = { __omitted: true }
export const omitted = constant(innerOmitted)
function isEmpty(object) {
2017-04-19 00:55:46 +00:00
return !Object.keys(object).length
}
function reduce(object, callback, initialValue) {
2017-04-19 00:55:46 +00:00
return Object.keys(object).reduce(
(acc, key) => callback(acc, object[key], key),
initialValue
)
}
function resolveUpdates(updates, object) {
2017-04-19 00:55:46 +00:00
return reduce(
updates,
(acc, value, key) => {
let updatedValue = value
if (
!Array.isArray(value) &&
value !== null &&
typeof value === 'object'
2017-04-19 00:55:46 +00:00
) {
updatedValue = update(value, object[key]) // eslint-disable-line no-use-before-define
} else if (typeof value === 'function') {
updatedValue = value(object[key])
}
if (object[key] !== updatedValue) {
acc[key] = updatedValue // eslint-disable-line no-param-reassign
}
return acc
},
{}
)
}
2015-08-05 07:27:56 +00:00
function updateArray(updates, object) {
2017-04-19 00:55:46 +00:00
const newArray = [...object]
2020-04-02 14:16:13 +00:00
Object.keys(updates).forEach((key) => {
2017-04-19 00:55:46 +00:00
newArray[key] = updates[key]
})
2017-04-19 00:55:46 +00:00
return newArray
}
/**
* Recursively update an object or array.
*
* Can update with values:
* update({ foo: 3 }, { foo: 1, bar: 2 });
* // => { foo: 3, bar: 2 }
*
* Or with a function:
* update({ foo: x => (x + 1) }, { foo: 2 });
* // => { foo: 3 }
2015-08-02 06:38:27 +00:00
*
2015-08-19 14:29:06 +00:00
* @function
* @name update
2015-08-02 06:38:27 +00:00
* @param {Object|Function} updates
* @param {Object|Array} object to update
* @return {Object|Array} new object with modifications
*/
function update(updates, object, ...args) {
if (typeof updates === 'function') {
2017-04-19 00:55:46 +00:00
return updates(object, ...args)
}
if (!isPlainObject(updates)) {
2017-04-19 00:55:46 +00:00
return updates
}
const defaultedObject =
typeof object === 'undefined' || object === null ? {} : object
2017-04-19 00:55:46 +00:00
const resolvedUpdates = resolveUpdates(updates, defaultedObject)
if (isEmpty(resolvedUpdates)) {
2017-04-19 00:55:46 +00:00
return defaultedObject
}
if (Array.isArray(defaultedObject)) {
2018-11-28 00:56:13 +00:00
return updateArray(resolvedUpdates, defaultedObject).filter(
2020-04-02 14:16:13 +00:00
(value) => value !== innerOmitted
2018-11-28 00:56:13 +00:00
)
}
2018-11-28 00:56:13 +00:00
return _omitBy(
{ ...defaultedObject, ...resolvedUpdates },
2020-04-02 14:16:13 +00:00
(value) => value === innerOmitted
2018-11-28 00:56:13 +00:00
)
}
2017-04-19 00:55:46 +00:00
export default wrap(update, 2)