Comparing version 2.7.10 to 2.7.11
842
lib/objob.js
'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; | ||
var _clone = require('./funcs/clone'); | ||
var _uniques = require('uniques'); | ||
var _clone2 = _interopRequireDefault(_clone); | ||
var _uniques2 = _interopRequireDefault(_uniques); | ||
var _expand = require('./funcs/expand'); | ||
var _typeOf = require('type-of'); | ||
var _expand2 = _interopRequireDefault(_expand); | ||
var _typeOf2 = _interopRequireDefault(_typeOf); | ||
var _equals = require('./funcs/equals'); | ||
var _stringContains = require('string-contains'); | ||
var _equals2 = _interopRequireDefault(_equals); | ||
var _stringContains2 = _interopRequireDefault(_stringContains); | ||
var _filter = require('./funcs/filter'); | ||
var _functions = require('./functions'); | ||
var _filter2 = _interopRequireDefault(_filter); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _flatten = require('./funcs/flatten'); | ||
/** | ||
* @namespace | ||
* @version 2.0.0 | ||
* */ | ||
var ob = { | ||
/** | ||
* Performs a deep clone of an object or array. | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* y = ob.clone(x) | ||
* | ||
* (x.a === y.a && x.d.f === y.d.f) | ||
* // → true | ||
* | ||
* y === x | ||
* // → false | ||
* | ||
* @param {object|any[]} subject The object or array to clone. | ||
* @returns {object|any[]} The cloned object or arraay | ||
*/ | ||
clone: function clone(subject) { | ||
if ((0, _typeOf2.default)(subject) === 'object' || (0, _typeOf2.default)(subject) === 'array') { | ||
return ob.expand(ob.flatten(subject)); | ||
} else { | ||
return subject; | ||
} | ||
}, | ||
/** | ||
* Returns an object without the given keys or an array with each object not having the given keys. | ||
* | ||
* @example | ||
* let x = { | ||
* c: 3, | ||
* d: {e: 4, f: [5,6]}, | ||
* g: [7, 8] | ||
* } | ||
* | ||
* ob.omit(x, ['d.e','d.f[].0','g[].1']); | ||
* // → { | ||
* // c:3, | ||
* // d: { | ||
* // f: [6] | ||
* // }, | ||
* // g:[7] | ||
* //} | ||
* | ||
* @example | ||
* let x = [ | ||
* 3, | ||
* {e: 4, f: [5,6]}, | ||
* [7, 8] | ||
* ] | ||
* | ||
* ob.omit(x, ['[]1.e','[]1.f[].0','[]2[].1']); | ||
* // → { | ||
* // 3, | ||
* // { | ||
* // f: [6] | ||
* // }, | ||
* // [7] | ||
* //} | ||
* | ||
* @param {object|any[]} subject The object or array to perform the omit operation on. | ||
* @param {string|string[]} input The keys or key of the object or nested object that you would like to omit. | ||
* @returns {object|any[]} The object or array of objects without the omited keys | ||
*/ | ||
omit: function omit(subject) { | ||
var input = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
var _flatten2 = _interopRequireDefault(_flatten); | ||
subject = ob.clone(subject); | ||
var flattened = ob.flatten(subject); | ||
var updatedFlattened = {}; | ||
var _omit = require('./funcs/omit'); | ||
for (var key in flattened) { | ||
if ((0, _typeOf2.default)(input) === 'array') { | ||
var matchFound = false; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
var _omit2 = _interopRequireDefault(_omit); | ||
try { | ||
for (var _iterator = input[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var inputKey = _step.value; | ||
var _keys = require('./funcs/keys'); | ||
var re = new RegExp('^' + inputKey + '\\..*', 'g'); | ||
if (key.match(re) || key === inputKey) { | ||
console.dir('Match found'); | ||
console.dir(key); | ||
matchFound = true; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
var _keys2 = _interopRequireDefault(_keys); | ||
if (matchFound === false) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} else { | ||
var re = new RegExp('^' + input + '\\..*', 'g'); | ||
if (!key.match(re) && key !== input) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} | ||
} | ||
return ob.expand(updatedFlattened); | ||
}, | ||
/** | ||
* Returns true if two objects or arrays have the same contents as one another. | ||
* | ||
* @example | ||
* | ||
* let x = { | ||
* a: 1, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* let y = { | ||
* a: 1, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* ob.equals(x, y) | ||
* // → true | ||
* | ||
* ob.equals([x, x], [y, y]) | ||
* // → true | ||
* | ||
* @param {object|any[]} subject The object or array to compare to | ||
* @param {object|any[]} subject2 The object or compare against | ||
* @returns {boolean} | ||
*/ | ||
equals: function equals(subject, subject2) { | ||
subject = ob.flatten(subject); | ||
subject2 = ob.flatten(subject2); | ||
var notEqual = false; | ||
var _mapValues = require('./funcs/mapValues'); | ||
if (Object.keys(subject).length !== Object.keys(subject2).length) { | ||
notEqual = true; | ||
} | ||
var _mapValues2 = _interopRequireDefault(_mapValues); | ||
var shallowSubject = (0, _functions.makeFlattenedShallow)(subject); | ||
var shallowSubject2 = (0, _functions.makeFlattenedShallow)(subject2); | ||
for (var key in Object.keys(shallowSubject)) { | ||
if (shallowSubject[key] !== shallowSubject2[key]) { | ||
notEqual = true; | ||
} | ||
} | ||
var _merge = require('./funcs/merge'); | ||
return !notEqual; | ||
}, | ||
/** | ||
* Takes a flattened object and expands it back to a full object or array of objects. | ||
* | ||
* @example | ||
* | ||
* let x = { | ||
* 'a.b.c': 1, | ||
* 'a.b.d': [2,3] | ||
* 'a.b.d[].0': 2, | ||
* 'a.b.d[].1': 3, | ||
* } | ||
* | ||
* ob.expand(x) | ||
* // → { | ||
* // a: { | ||
* // b: { | ||
* // c: 1, | ||
* // d: [2,3] | ||
* // }}} | ||
* | ||
* @example | ||
* let x = { | ||
* '[]0[].0.a.b.c': 1, | ||
* '[]1.b.d': [2,3] | ||
* '[]1.b.d[].0': 2 | ||
* '[]1.b.d[].1': 3 | ||
* } | ||
* | ||
* ob.expand(x) | ||
* // → [ | ||
* // [{ | ||
* // a: { | ||
* // b: { | ||
* // c: 1, | ||
* // }, | ||
* // }, | ||
* // }], | ||
* // { | ||
* // b: { | ||
* // d: [2,3] | ||
* // } | ||
* // } | ||
* //] | ||
* | ||
* @param {object} subject The object to expand | ||
* @returns {object|any[]} The expanded object or array of objects. | ||
*/ | ||
expand: function expand(subject) { | ||
var depth = arguments.length <= 1 || arguments[1] === undefined ? 1 : arguments[1]; | ||
var _merge2 = _interopRequireDefault(_merge); | ||
var res = undefined; | ||
subject = (0, _functions.makeFlattenedShallow)(subject); | ||
var _pick = require('./funcs/pick'); | ||
var keyChains = ob.keys(subject); | ||
var _pick2 = _interopRequireDefault(_pick); | ||
var isArray = false; | ||
if (true) { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
var _values = require('./funcs/values'); | ||
try { | ||
for (var _iterator2 = keyChains[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var i = _step2.value; | ||
var _values2 = _interopRequireDefault(_values); | ||
if (i.startsWith('[]')) { | ||
isArray = true; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// if array, things need to be handled just a little bit differently | ||
if (isArray) { | ||
res = []; | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
try { | ||
for (var _iterator3 = keyChains[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var keyChain = _step3.value; | ||
// This converts something like []0.name.name or []0[].name.name to 0 | ||
var firstKey = keyChain.split('.')[0]; // eg []0[] | ||
var fullIndex = firstKey.substr(2); // eg. 0[] | ||
var index = fullIndex.replace('[]', ''); // eg 0 | ||
var nestedKeyChain = keyChain.replace(firstKey + '.', ''); | ||
var tmp = {}; | ||
// Make sure tmp is set correctly based on the object type | ||
if ((0, _typeOf2.default)(res[index]) === 'array' || fullIndex.endsWith('[]')) { | ||
tmp['[]' + nestedKeyChain] = subject[keyChain]; | ||
} else { | ||
tmp[nestedKeyChain] = subject[keyChain]; | ||
} | ||
if (keyChain.split('.').length === 1) { | ||
// If there is no nested data just add to the array | ||
res[index] = subject[keyChain]; | ||
} else if ((0, _typeOf2.default)(res[index]) === 'object' || (0, _typeOf2.default)(res[index]) === 'array') { | ||
// If the next keyChain is an object | ||
res[index] = ob.merge(res[index], ob.expand(tmp, depth + 1)); | ||
} else if (fullIndex.endsWith('[]')) { | ||
res[index] = ob.expand(tmp, depth + 1); | ||
} else { | ||
res[index] = ob.expand(tmp, depth + 1); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
} else if (keyChains.length === 1) { | ||
// When the object is just {'example.example': y} | ||
// One key and one value | ||
var tmp = {}; | ||
var keyChain = keyChains[0]; // something like 'first.another.another' | ||
var value = subject[keyChain]; | ||
var count = undefined; | ||
res = tmp; // Pointing to tmp so that we have a place holder before nesting | ||
count = 1; | ||
var keys = keyChain.split('.'); | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
try { | ||
for (var _iterator4 = keys[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var key = _step4.value; | ||
if (count === keys.length) { | ||
tmp[key.replace('[]', '')] = value; | ||
} else { | ||
var _isArray = (0, _stringContains2.default)(key, '[]'); | ||
if (_isArray) { | ||
key = key.replace('[]', ''); | ||
tmp[key] = []; | ||
} else { | ||
tmp[key] = {}; | ||
} | ||
tmp = tmp[key]; | ||
} | ||
count++; | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
} else { | ||
// If multiple keychains in the object, simplify our logic a bit | ||
res = {}; | ||
for (var i in subject) { | ||
var tmp = {}; | ||
tmp[i] = subject[i]; | ||
res = ob.merge(res, ob.expand(tmp, depth + 1)); | ||
} | ||
} | ||
return res; | ||
}, | ||
/** | ||
* Takes an object and return a flattened representation of that object that has one level of depth. This allows you to do complex operations on your object while it's in a format that's easier to work with. | ||
* | ||
* @example | ||
* let x = { | ||
* a:{ | ||
* b:{ | ||
* c: 1, | ||
* d: [2,3] | ||
* } | ||
* } | ||
* } | ||
* | ||
* ob.flatten(x) | ||
* // → { | ||
* // 'a.b.c': 1, | ||
* // 'a.b.d': [2,3] | ||
* // 'a.b.d[].0': 2, | ||
* // 'a.b.d[].1': 3', | ||
* //} | ||
* | ||
* @example | ||
* let x = [ | ||
* [{ | ||
* a: { | ||
* b: { | ||
* c: 1, | ||
* }, | ||
* }, | ||
* }], | ||
* { | ||
* b: { | ||
* d: [2,3] | ||
* } | ||
* } | ||
* ] | ||
* | ||
* // → { | ||
* // '[]0[].0.a.b.c': 1, | ||
* // '[]1.b.d': [2,3] | ||
* // '[]1.b.d[].0': 2 | ||
* // '[]1.b.d[].1': 3 | ||
* //} | ||
* | ||
* | ||
* @param {object|any[]} subject The object or array of objects to perform the flattening on | ||
* @returns {object} The flat representation of the object | ||
*/ | ||
flatten: function flatten(subject) { | ||
var prefix = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1]; | ||
var depth = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2]; | ||
var res = {}; | ||
if ((0, _typeOf2.default)(subject) === 'object' || (0, _typeOf2.default)(subject) === 'array') { | ||
for (var i in subject) { | ||
var tmpPrefix = undefined; | ||
if (prefix === '') { | ||
tmpPrefix = '' + i; | ||
} else { | ||
tmpPrefix = prefix + '.' + i; | ||
} | ||
// If we're dealing with an array at the top level, we need to prefix it with [] to make it clear that we're dealing with | ||
// an array as opposed to an object | ||
if (depth === 1 && (0, _typeOf2.default)(subject) === 'array') { | ||
tmpPrefix = '[]' + tmpPrefix; | ||
} | ||
if ((0, _typeOf2.default)(subject[i]) === 'array') { | ||
tmpPrefix = tmpPrefix + '[]'; | ||
} | ||
res[tmpPrefix] = subject[i]; | ||
if ((0, _typeOf2.default)(subject[i]) === 'array' || (0, _typeOf2.default)(subject[i]) === 'object') { | ||
res = ob.merge(res, ob.flatten(subject[i], tmpPrefix, depth + 1)); | ||
} | ||
} | ||
} | ||
return res; | ||
}, | ||
/** | ||
* Return all keys for an object recursively, including keys in objects that are in arrays. | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3 | ||
* } | ||
* | ||
* ob.keys(x) | ||
* // → ['a','b','c'] | ||
* | ||
* @example | ||
* let x = [{ a: 1, b: 2, c: 3}, {d: 1}] | ||
* | ||
* ob.keys(x) | ||
* // → ['a','b','c', 'd'] | ||
* | ||
* @param {object|any[]} subject The object or array of objects whose keys you wish to retrieve. | ||
* @param {boolean} [unique=true] Whether the result should contain duplicates or not | ||
* @returns {string[]} The keys | ||
*/ | ||
keys: function keys(subject) { | ||
var unique = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; | ||
var keys = []; | ||
if ((0, _typeOf2.default)(subject) === 'array') { | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
try { | ||
for (var _iterator5 = subject[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var i = _step5.value; | ||
keys = keys.concat(ob.keys(i)); | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
} | ||
} | ||
} else if ((0, _typeOf2.default)(subject) === 'object') { | ||
for (var k in subject) { | ||
keys = keys.concat(ob.keys(k)); | ||
}; | ||
} else { | ||
keys.push(subject); | ||
} | ||
if (unique) { | ||
return (0, _uniques2.default)(keys); | ||
} else { | ||
return keys; | ||
} | ||
}, | ||
/** | ||
* Removes all keys with undefined values from an object and/or arrays of objects. | ||
* | ||
* @example | ||
* let x = { | ||
* a: undefined, | ||
* b: { | ||
* c: undefined, | ||
* d: 2, | ||
* }, | ||
* e: [undefined, 1, 2] | ||
* } | ||
* | ||
* ob.filter(x, (x) => x !== undefined)) | ||
* // → { | ||
* // b: { | ||
* // d: 2, | ||
* // }, | ||
* // e: [1, 2] | ||
* //} | ||
* | ||
* | ||
* @param {object|any[]} subject The object or array of objects you would like to remove undefined values from. | ||
* @param {function} validate The function to perform for the filter. | ||
* @returns {object|any[]} The object or array of objects without any undefined values | ||
*/ | ||
filter: function filter(subject, validate) { | ||
subject = ob.clone(subject); | ||
var res = undefined; | ||
if ((0, _typeOf2.default)(subject) === 'array') { | ||
res = []; | ||
for (var key in subject) { | ||
if (validate(subject[key])) { | ||
res.push(ob.filter(subject[key], validate)); | ||
} | ||
} | ||
subject = res; | ||
} else if ((0, _typeOf2.default)(subject) === 'object') { | ||
for (var key in subject) { | ||
if (validate(subject[key]) === true) { | ||
subject[key] = ob.filter(subject[key], validate); | ||
} else { | ||
delete subject[key]; | ||
} | ||
} | ||
} | ||
return subject; | ||
}, | ||
/** | ||
* Returns the object with each value being run throuh the function. (Better description needed. lol) | ||
* | ||
* @example | ||
* | ||
* let x = { | ||
* a: 1, | ||
* d: {f: 4, g: [1,2,3]} | ||
* } | ||
* | ||
* ob.mapValues(x, (x) => x*3 ) | ||
* // → { | ||
* // a: 3, | ||
* // d: {f: 12, g: [3,6,9]} | ||
* //} | ||
* @param {object|any[]} subject The object or array to compare to | ||
* @param {function} func The function to operate on each value | ||
* @returns {object|any[]} | ||
*/ | ||
mapValues: function mapValues(subject, func) { | ||
subject = ob.flatten(subject); | ||
var shallowSubject = (0, _functions.makeFlattenedShallow)(subject); | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator6 = Object.keys(shallowSubject)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var key = _step6.value; | ||
if ((0, _typeOf2.default)(shallowSubject[key]) !== 'object' && (0, _typeOf2.default)(shallowSubject[key]) !== 'array') { | ||
shallowSubject[key] = func(shallowSubject[key]); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
} | ||
return ob.expand(shallowSubject); | ||
}, | ||
/** | ||
* Merges the enumerable attributes of two objects deeply. | ||
* | ||
* @example | ||
* let x = { | ||
* a: {b: 1}, | ||
* } | ||
* | ||
* let y = { | ||
* a: {c: 1}, | ||
* } | ||
* | ||
* ob.merge(x, y); | ||
* // → {a: {b: 1, c:1}} | ||
* | ||
* @param {object|any[]} target The object or array of objects to merge into | ||
* @param {object|any[]} src The object or array of objects to merge from | ||
* @returns {object|any[]} The merged object or array | ||
*/ | ||
merge: function merge(target, src) { | ||
var array = Array.isArray(src); | ||
var dst = array && [] || {}; | ||
if (array) { | ||
target = target || []; | ||
dst = dst.concat(target); | ||
src.forEach(function (e, i) { | ||
if (typeof dst[i] === 'undefined') { | ||
dst[i] = e; | ||
} else if ((typeof e === 'undefined' ? 'undefined' : _typeof(e)) === 'object') { | ||
dst[i] = ob.merge(target[i], e); | ||
} else { | ||
if (target.indexOf(e) === -1) { | ||
dst.push(e); | ||
} | ||
} | ||
}); | ||
} else { | ||
if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object') { | ||
Object.keys(target).forEach(function (key) { | ||
dst[key] = target[key]; | ||
}); | ||
} | ||
Object.keys(src).forEach(function (key) { | ||
if (_typeof(src[key]) !== 'object' || !src[key]) { | ||
dst[key] = src[key]; | ||
} else { | ||
if (!target[key]) { | ||
dst[key] = src[key]; | ||
} else { | ||
dst[key] = ob.merge(target[key], src[key]); | ||
} | ||
} | ||
}); | ||
} | ||
return dst; | ||
}, | ||
/** | ||
* Returns an object only with the given keys. If an array is passed, it will return an array of each given object only having the picked keys. | ||
* | ||
* @example | ||
* let x = { | ||
* c: 3, | ||
* d: {e: 4, f: [5,6]}, | ||
* g: [7, 8] | ||
* } | ||
* | ||
* ob.pick(x, ['d.e','d.f[].0','g[].1']); | ||
* // → {d: {e: 4, f: [5]}, g: [8]} | ||
* | ||
* @param {object|any[]} subject The object or array of objects to perform the pick operation on | ||
* @param {string|string[]} input The keys or key you would like to pick | ||
* @returns {object|any[]} The object or array of objects with only the picked keys. | ||
*/ | ||
pick: function pick(subject) { | ||
var input = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
subject = ob.clone(subject); | ||
var flattened = ob.flatten(subject); | ||
var updatedFlattened = {}; | ||
for (var key in flattened) { | ||
if ((0, _typeOf2.default)(input) === 'array') { | ||
var matchFound = false; | ||
var _iteratorNormalCompletion7 = true; | ||
var _didIteratorError7 = false; | ||
var _iteratorError7 = undefined; | ||
try { | ||
for (var _iterator7 = input[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { | ||
var inputKey = _step7.value; | ||
var re = new RegExp('^'.inputKey + '\\..*', 'g'); | ||
if (key.match(re) || key === inputKey) { | ||
matchFound = true; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError7 = true; | ||
_iteratorError7 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion7 && _iterator7.return) { | ||
_iterator7.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError7) { | ||
throw _iteratorError7; | ||
} | ||
} | ||
} | ||
if (matchFound === true) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} else { | ||
var re = new RegExp('^' + input + '\\.', 'g'); | ||
if (key.match(re) || key === input) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} | ||
} | ||
return ob.expand(updatedFlattened); | ||
}, | ||
/** | ||
* Returns all values for a given object or array recursively. | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3 | ||
* } | ||
* | ||
* ob.values(x) | ||
* // → [1, 2, 3] | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* d: [4] | ||
* } | ||
* | ||
* ob.values(x) | ||
* // → [1, 2, 3, 4] | ||
* | ||
* @param {object|any[]} subject The object or array of objects to get the values of | ||
* @param {boolean} [unique=true] Whether the result should contain duplicates or not | ||
* @returns {any[]} | ||
*/ | ||
values: function values(subject) { | ||
var unique = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; | ||
var values = []; | ||
if ((0, _typeOf2.default)(subject) === 'array') { | ||
var _iteratorNormalCompletion8 = true; | ||
var _didIteratorError8 = false; | ||
var _iteratorError8 = undefined; | ||
try { | ||
for (var _iterator8 = subject[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { | ||
var i = _step8.value; | ||
values = values.concat(ob.values(i)); | ||
} | ||
} catch (err) { | ||
_didIteratorError8 = true; | ||
_iteratorError8 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion8 && _iterator8.return) { | ||
_iterator8.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError8) { | ||
throw _iteratorError8; | ||
} | ||
} | ||
} | ||
} else if ((0, _typeOf2.default)(subject) === 'object') { | ||
for (var k in subject) { | ||
values = values.concat(ob.values(subject[k])); | ||
}; | ||
} else { | ||
values.push(subject); | ||
} | ||
if (unique) { | ||
return (0, _uniques2.default)(values); | ||
} else { | ||
return values; | ||
} | ||
} | ||
/** | ||
* @namespace | ||
* @version 2.0.0 | ||
* */ | ||
var ob = { | ||
clone: _clone2.default, | ||
expand: _expand2.default, | ||
equals: _equals2.default, | ||
filter: _filter2.default, | ||
flatten: _flatten2.default, | ||
keys: _keys2.default, | ||
mapValues: _mapValues2.default, | ||
merge: _merge2.default, | ||
omit: _omit2.default, | ||
pick: _pick2.default, | ||
values: _values2.default | ||
}; | ||
@@ -830,0 +66,0 @@ |
{ | ||
"name": "objob", | ||
"version": "2.7.10", | ||
"version": "2.7.11", | ||
"description": "A tool for controlling and manipulating javascript object fields and output.", | ||
@@ -5,0 +5,0 @@ "main": "lib/objob.js", |
652
src/objob.js
'use strict'; | ||
import uniques from 'uniques'; | ||
import type from 'type-of'; | ||
import contains from 'string-contains'; | ||
import { makeFlattenedShallow } from './functions'; | ||
import clone from './funcs/clone'; | ||
import expand from './funcs/expand'; | ||
import equals from './funcs/equals'; | ||
import filter from './funcs/filter'; | ||
import flatten from './funcs/flatten'; | ||
import omit from './funcs/omit'; | ||
import keys from './funcs/keys'; | ||
import mapValues from './funcs/mapValues'; | ||
import merge from './funcs/merge'; | ||
import pick from './funcs/pick'; | ||
import values from './funcs/values'; | ||
@@ -13,628 +20,13 @@ /** | ||
let ob = { | ||
/** | ||
* Performs a deep clone of an object or array. | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* y = ob.clone(x) | ||
* | ||
* (x.a === y.a && x.d.f === y.d.f) | ||
* // → true | ||
* | ||
* y === x | ||
* // → false | ||
* | ||
* @param {object|any[]} subject The object or array to clone. | ||
* @returns {object|any[]} The cloned object or arraay | ||
*/ | ||
clone: function(subject){ | ||
if(type(subject) === 'object' || type(subject) === 'array') { | ||
return ob.expand(ob.flatten(subject)); | ||
} else { | ||
return subject; | ||
} | ||
}, | ||
/** | ||
* Returns an object without the given keys or an array with each object not having the given keys. | ||
* | ||
* @example | ||
* let x = { | ||
* c: 3, | ||
* d: {e: 4, f: [5,6]}, | ||
* g: [7, 8] | ||
* } | ||
* | ||
* ob.omit(x, ['d.e','d.f[].0','g[].1']); | ||
* // → { | ||
* // c:3, | ||
* // d: { | ||
* // f: [6] | ||
* // }, | ||
* // g:[7] | ||
* //} | ||
* | ||
* @example | ||
* let x = [ | ||
* 3, | ||
* {e: 4, f: [5,6]}, | ||
* [7, 8] | ||
* ] | ||
* | ||
* ob.omit(x, ['[]1.e','[]1.f[].0','[]2[].1']); | ||
* // → { | ||
* // 3, | ||
* // { | ||
* // f: [6] | ||
* // }, | ||
* // [7] | ||
* //} | ||
* | ||
* @param {object|any[]} subject The object or array to perform the omit operation on. | ||
* @param {string|string[]} input The keys or key of the object or nested object that you would like to omit. | ||
* @returns {object|any[]} The object or array of objects without the omited keys | ||
*/ | ||
omit: function(subject, input = []){ | ||
subject = ob.clone(subject); | ||
let flattened = ob.flatten(subject); | ||
let updatedFlattened = {}; | ||
for(let key in flattened) { | ||
if(type(input) === 'array') { | ||
let matchFound = false; | ||
for(let inputKey of input) { | ||
let re = new RegExp('^'+inputKey+'\\..*','g'); | ||
if(key.match(re) || key === inputKey) { | ||
console.dir('Match found'); | ||
console.dir(key); | ||
matchFound = true; | ||
} | ||
} | ||
if(matchFound === false) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} else { | ||
let re = new RegExp('^'+input+'\\..*','g'); | ||
if(!key.match(re) && key !== input) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} | ||
} | ||
return ob.expand(updatedFlattened); | ||
}, | ||
/** | ||
* Returns true if two objects or arrays have the same contents as one another. | ||
* | ||
* @example | ||
* | ||
* let x = { | ||
* a: 1, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* let y = { | ||
* a: 1, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* ob.equals(x, y) | ||
* // → true | ||
* | ||
* ob.equals([x, x], [y, y]) | ||
* // → true | ||
* | ||
* @param {object|any[]} subject The object or array to compare to | ||
* @param {object|any[]} subject2 The object or compare against | ||
* @returns {boolean} | ||
*/ | ||
equals: function(subject, subject2){ | ||
subject = ob.flatten(subject); | ||
subject2 = ob.flatten(subject2); | ||
let notEqual = false; | ||
if(Object.keys(subject).length !== Object.keys(subject2).length) { | ||
notEqual = true; | ||
} | ||
let shallowSubject = makeFlattenedShallow(subject); | ||
let shallowSubject2 = makeFlattenedShallow(subject2); | ||
for(let key in Object.keys(shallowSubject)) { | ||
if(shallowSubject[key] !== shallowSubject2[key]) { | ||
notEqual = true; | ||
} | ||
} | ||
return !notEqual; | ||
}, | ||
/** | ||
* Takes a flattened object and expands it back to a full object or array of objects. | ||
* | ||
* @example | ||
* | ||
* let x = { | ||
* 'a.b.c': 1, | ||
* 'a.b.d': [2,3] | ||
* 'a.b.d[].0': 2, | ||
* 'a.b.d[].1': 3, | ||
* } | ||
* | ||
* ob.expand(x) | ||
* // → { | ||
* // a: { | ||
* // b: { | ||
* // c: 1, | ||
* // d: [2,3] | ||
* // }}} | ||
* | ||
* @example | ||
* let x = { | ||
* '[]0[].0.a.b.c': 1, | ||
* '[]1.b.d': [2,3] | ||
* '[]1.b.d[].0': 2 | ||
* '[]1.b.d[].1': 3 | ||
* } | ||
* | ||
* ob.expand(x) | ||
* // → [ | ||
* // [{ | ||
* // a: { | ||
* // b: { | ||
* // c: 1, | ||
* // }, | ||
* // }, | ||
* // }], | ||
* // { | ||
* // b: { | ||
* // d: [2,3] | ||
* // } | ||
* // } | ||
* //] | ||
* | ||
* @param {object} subject The object to expand | ||
* @returns {object|any[]} The expanded object or array of objects. | ||
*/ | ||
expand: function(subject, depth = 1){ | ||
let res; | ||
subject = makeFlattenedShallow(subject); | ||
let keyChains = ob.keys(subject); | ||
let isArray = false; | ||
if(true) { | ||
for(let i of keyChains) { | ||
if(i.startsWith('[]')) { | ||
isArray = true; | ||
} | ||
} | ||
} | ||
// if array, things need to be handled just a little bit differently | ||
if(isArray) { | ||
res = []; | ||
for(let keyChain of keyChains) { | ||
// This converts something like []0.name.name or []0[].name.name to 0 | ||
const firstKey = keyChain.split('.')[0]; // eg []0[] | ||
const fullIndex = firstKey.substr(2); // eg. 0[] | ||
const index = fullIndex.replace('[]', ''); // eg 0 | ||
const nestedKeyChain = keyChain.replace(firstKey + '.', ''); | ||
let tmp = {}; | ||
// Make sure tmp is set correctly based on the object type | ||
if(type(res[index]) === 'array' || fullIndex.endsWith('[]')) { | ||
tmp['[]'+nestedKeyChain] = subject[keyChain]; | ||
} else { | ||
tmp[nestedKeyChain] = subject[keyChain]; | ||
} | ||
if(keyChain.split('.').length === 1) { | ||
// If there is no nested data just add to the array | ||
res[index] = subject[keyChain]; | ||
} else if(type(res[index]) === 'object' || type(res[index]) === 'array') { | ||
// If the next keyChain is an object | ||
res[index] = ob.merge(res[index], ob.expand(tmp, depth+1)); | ||
} else if(fullIndex.endsWith('[]')) { | ||
res[index] = ob.expand(tmp, depth+1); | ||
} else { | ||
res[index] = ob.expand(tmp, depth+1); | ||
} | ||
} | ||
} else if(keyChains.length === 1) { | ||
// When the object is just {'example.example': y} | ||
// One key and one value | ||
let tmp = {}; | ||
let keyChain = keyChains[0]; // something like 'first.another.another' | ||
let value = subject[keyChain]; | ||
let count; | ||
res = tmp; // Pointing to tmp so that we have a place holder before nesting | ||
count = 1; | ||
let keys = keyChain.split('.'); | ||
for(let key of keys) { | ||
if(count === keys.length) { | ||
tmp[key.replace('[]','')] = value; | ||
} else { | ||
let isArray = contains(key, '[]'); | ||
if(isArray) { | ||
key = key.replace('[]',''); | ||
tmp[key] = []; | ||
} else { | ||
tmp[key] = {}; | ||
} | ||
tmp = tmp[key]; | ||
} | ||
count++; | ||
} | ||
} else { | ||
// If multiple keychains in the object, simplify our logic a bit | ||
res = {}; | ||
for(let i in subject) { | ||
let tmp = {}; | ||
tmp[i] = subject[i]; | ||
res = ob.merge(res, ob.expand(tmp, depth+1)); | ||
} | ||
} | ||
return res; | ||
}, | ||
/** | ||
* Takes an object and return a flattened representation of that object that has one level of depth. This allows you to do complex operations on your object while it's in a format that's easier to work with. | ||
* | ||
* @example | ||
* let x = { | ||
* a:{ | ||
* b:{ | ||
* c: 1, | ||
* d: [2,3] | ||
* } | ||
* } | ||
* } | ||
* | ||
* ob.flatten(x) | ||
* // → { | ||
* // 'a.b.c': 1, | ||
* // 'a.b.d': [2,3] | ||
* // 'a.b.d[].0': 2, | ||
* // 'a.b.d[].1': 3', | ||
* //} | ||
* | ||
* @example | ||
* let x = [ | ||
* [{ | ||
* a: { | ||
* b: { | ||
* c: 1, | ||
* }, | ||
* }, | ||
* }], | ||
* { | ||
* b: { | ||
* d: [2,3] | ||
* } | ||
* } | ||
* ] | ||
* | ||
* // → { | ||
* // '[]0[].0.a.b.c': 1, | ||
* // '[]1.b.d': [2,3] | ||
* // '[]1.b.d[].0': 2 | ||
* // '[]1.b.d[].1': 3 | ||
* //} | ||
* | ||
* | ||
* @param {object|any[]} subject The object or array of objects to perform the flattening on | ||
* @returns {object} The flat representation of the object | ||
*/ | ||
flatten: function(subject, prefix='', depth = 1){ | ||
let res = {}; | ||
if(type(subject) === 'object' || type(subject) === 'array'){ | ||
for(let i in subject) { | ||
let tmpPrefix; | ||
if(prefix === '') { | ||
tmpPrefix = `${i}`; | ||
} else { | ||
tmpPrefix = `${prefix}.${i}`; | ||
} | ||
// If we're dealing with an array at the top level, we need to prefix it with [] to make it clear that we're dealing with | ||
// an array as opposed to an object | ||
if(depth === 1 && type(subject) === 'array') { | ||
tmpPrefix = `[]${tmpPrefix}`; | ||
} | ||
if(type(subject[i]) === 'array') { | ||
tmpPrefix = tmpPrefix + '[]'; | ||
} | ||
res[tmpPrefix] = subject[i]; | ||
if(type(subject[i]) === 'array' || type(subject[i]) === 'object') { | ||
res = ob.merge(res, ob.flatten(subject[i],tmpPrefix, depth+1)); | ||
} | ||
} | ||
} | ||
return res; | ||
}, | ||
/** | ||
* Return all keys for an object recursively, including keys in objects that are in arrays. | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3 | ||
* } | ||
* | ||
* ob.keys(x) | ||
* // → ['a','b','c'] | ||
* | ||
* @example | ||
* let x = [{ a: 1, b: 2, c: 3}, {d: 1}] | ||
* | ||
* ob.keys(x) | ||
* // → ['a','b','c', 'd'] | ||
* | ||
* @param {object|any[]} subject The object or array of objects whose keys you wish to retrieve. | ||
* @param {boolean} [unique=true] Whether the result should contain duplicates or not | ||
* @returns {string[]} The keys | ||
*/ | ||
keys:function(subject, unique=true) { | ||
let keys = []; | ||
if(type(subject) === 'array') { | ||
for(let i of subject){ | ||
keys = keys.concat(ob.keys(i)); | ||
} | ||
} else if(type(subject) === 'object') { | ||
for(let k in subject) { | ||
keys = keys.concat(ob.keys(k)); | ||
}; | ||
} else { | ||
keys.push(subject); | ||
} | ||
if(unique) { | ||
return uniques(keys); | ||
} else { | ||
return keys; | ||
} | ||
}, | ||
/** | ||
* Removes all keys with undefined values from an object and/or arrays of objects. | ||
* | ||
* @example | ||
* let x = { | ||
* a: undefined, | ||
* b: { | ||
* c: undefined, | ||
* d: 2, | ||
* }, | ||
* e: [undefined, 1, 2] | ||
* } | ||
* | ||
* ob.filter(x, (x) => x !== undefined)) | ||
* // → { | ||
* // b: { | ||
* // d: 2, | ||
* // }, | ||
* // e: [1, 2] | ||
* //} | ||
* | ||
* | ||
* @param {object|any[]} subject The object or array of objects you would like to remove undefined values from. | ||
* @param {function} validate The function to perform for the filter. | ||
* @returns {object|any[]} The object or array of objects without any undefined values | ||
*/ | ||
filter: (subject, validate) => { | ||
subject = ob.clone(subject); | ||
let res; | ||
if(type(subject) === 'array') { | ||
res = []; | ||
for(let key in subject) { | ||
if(validate(subject[key])) { | ||
res.push(ob.filter(subject[key], validate)); | ||
} | ||
} | ||
subject = res; | ||
} else if(type(subject) === 'object') { | ||
for(let key in subject) { | ||
if(validate(subject[key]) === true ) { | ||
subject[key] = ob.filter(subject[key], validate); | ||
} else { | ||
delete subject[key]; | ||
} | ||
} | ||
} | ||
return subject; | ||
}, | ||
/** | ||
* Returns the object with each value being run throuh the function. (Better description needed. lol) | ||
* | ||
* @example | ||
* | ||
* let x = { | ||
* a: 1, | ||
* d: {f: 4, g: [1,2,3]} | ||
* } | ||
* | ||
* ob.mapValues(x, (x) => x*3 ) | ||
* // → { | ||
* // a: 3, | ||
* // d: {f: 12, g: [3,6,9]} | ||
* //} | ||
* @param {object|any[]} subject The object or array to compare to | ||
* @param {function} func The function to operate on each value | ||
* @returns {object|any[]} | ||
*/ | ||
mapValues: function(subject, func){ | ||
subject = ob.flatten(subject); | ||
let shallowSubject = makeFlattenedShallow(subject); | ||
for(let key of Object.keys(shallowSubject)) { | ||
if(type(shallowSubject[key]) !== 'object' && type(shallowSubject[key]) !== 'array') { | ||
shallowSubject[key] = func(shallowSubject[key]); | ||
} | ||
} | ||
return ob.expand(shallowSubject); | ||
}, | ||
/** | ||
* Merges the enumerable attributes of two objects deeply. | ||
* | ||
* @example | ||
* let x = { | ||
* a: {b: 1}, | ||
* } | ||
* | ||
* let y = { | ||
* a: {c: 1}, | ||
* } | ||
* | ||
* ob.merge(x, y); | ||
* // → {a: {b: 1, c:1}} | ||
* | ||
* @param {object|any[]} target The object or array of objects to merge into | ||
* @param {object|any[]} src The object or array of objects to merge from | ||
* @returns {object|any[]} The merged object or array | ||
*/ | ||
merge: function(target, src) { | ||
let array = Array.isArray(src); | ||
let dst = array && [] || {}; | ||
if (array) { | ||
target = target || []; | ||
dst = dst.concat(target); | ||
src.forEach((e, i) => { | ||
if (typeof dst[i] === 'undefined') { | ||
dst[i] = e; | ||
} else if (typeof e === 'object') { | ||
dst[i] = ob.merge(target[i], e); | ||
} else { | ||
if (target.indexOf(e) === -1) { | ||
dst.push(e); | ||
} | ||
} | ||
}); | ||
} else { | ||
if (target && typeof target === 'object') { | ||
Object.keys(target).forEach((key) => { | ||
dst[key] = target[key]; | ||
}); | ||
} | ||
Object.keys(src).forEach((key) => { | ||
if (typeof src[key] !== 'object' || !src[key]) { | ||
dst[key] = src[key]; | ||
} | ||
else { | ||
if (!target[key]) { | ||
dst[key] = src[key]; | ||
} else { | ||
dst[key] = ob.merge(target[key], src[key]); | ||
} | ||
} | ||
}); | ||
} | ||
return dst; | ||
}, | ||
/** | ||
* Returns an object only with the given keys. If an array is passed, it will return an array of each given object only having the picked keys. | ||
* | ||
* @example | ||
* let x = { | ||
* c: 3, | ||
* d: {e: 4, f: [5,6]}, | ||
* g: [7, 8] | ||
* } | ||
* | ||
* ob.pick(x, ['d.e','d.f[].0','g[].1']); | ||
* // → {d: {e: 4, f: [5]}, g: [8]} | ||
* | ||
* @param {object|any[]} subject The object or array of objects to perform the pick operation on | ||
* @param {string|string[]} input The keys or key you would like to pick | ||
* @returns {object|any[]} The object or array of objects with only the picked keys. | ||
*/ | ||
pick: (subject, input = []) => { | ||
subject = ob.clone(subject); | ||
let flattened = ob.flatten(subject); | ||
let updatedFlattened = {}; | ||
for(let key in flattened) { | ||
if(type(input) === 'array') { | ||
let matchFound = false; | ||
for(let inputKey of input) { | ||
let re = new RegExp('^'.inputKey+'\\..*','g'); | ||
if(key.match(re) || key === inputKey) { | ||
matchFound = true; | ||
} | ||
} | ||
if(matchFound === true) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} else { | ||
let re = new RegExp('^'+input+'\\.','g'); | ||
if(key.match(re) || key === input) { | ||
updatedFlattened[key] = flattened[key]; | ||
} | ||
} | ||
} | ||
return ob.expand(updatedFlattened); | ||
}, | ||
/** | ||
* Returns all values for a given object or array recursively. | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3 | ||
* } | ||
* | ||
* ob.values(x) | ||
* // → [1, 2, 3] | ||
* | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* d: [4] | ||
* } | ||
* | ||
* ob.values(x) | ||
* // → [1, 2, 3, 4] | ||
* | ||
* @param {object|any[]} subject The object or array of objects to get the values of | ||
* @param {boolean} [unique=true] Whether the result should contain duplicates or not | ||
* @returns {any[]} | ||
*/ | ||
values:(subject, unique=true) => { | ||
let values = []; | ||
if(type(subject) === 'array') { | ||
for(let i of subject){ | ||
values = values.concat(ob.values(i)); | ||
} | ||
} else if(type(subject) === 'object') { | ||
for(let k in subject) { | ||
values = values.concat(ob.values(subject[k])); | ||
}; | ||
} else { | ||
values.push(subject); | ||
} | ||
if(unique) { | ||
return uniques(values); | ||
} else { | ||
return values; | ||
} | ||
}, | ||
clone, | ||
expand, | ||
equals, | ||
filter, | ||
flatten, | ||
keys, | ||
mapValues, | ||
merge, | ||
omit, | ||
pick, | ||
values, | ||
}; | ||
@@ -641,0 +33,0 @@ |
667676
65
3453