Comparing version 2.3.0 to 2.4.0
{ | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"tags": { | ||
@@ -4,0 +4,0 @@ "allowUnknownTags" : true |
632
lib/objob.js
@@ -15,6 +15,2 @@ 'use strict'; | ||
var _deepmerge = require('deepmerge'); | ||
var _deepmerge2 = _interopRequireDefault(_deepmerge); | ||
var _functions = require('./functions'); | ||
@@ -24,2 +20,4 @@ | ||
function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; } | ||
/** | ||
@@ -36,28 +34,27 @@ * @namespace | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* y = ob.clone(x) | ||
* | ||
* (x.a === y.a && x.d.f === y.d.f) | ||
* // → true | ||
* | ||
* y === x | ||
* // → false | ||
* | ||
* @param {object|object[]} subject The object or array to clone. | ||
* @returns {object|object[]} The cloned object or arraay | ||
* @param {object|any[]} subject The object or array to clone. | ||
* @returns {object|any[]} The cloned object or arraay | ||
*/ | ||
clone: function clone(subject) { | ||
return ob.expand(ob.flatten(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 <caption>Basic usage.</caption> | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* } | ||
* | ||
* ob.deselect(x, ['a','b']); | ||
* // → {c: 3} | ||
* @example <caption>Advanced usage.</caption> | ||
* @example | ||
* let x = { | ||
@@ -69,13 +66,35 @@ * c: 3, | ||
* | ||
* ob.deselect(x, ['d.e','d.f[].0','g[].1']); | ||
* // → {c: 3, d: {f:[6]}, g:[7]} | ||
* ob.omit(x, ['d.e','d.f[].0','g[].1']); | ||
* // → { | ||
* // c:3, | ||
* // d: { | ||
* // f: [6] | ||
* // }, | ||
* // g:[7] | ||
* //} | ||
* | ||
* @param {object|object[]} subject The object or array to perform the deselect operation on. | ||
* @param {string[]} keys The keys of the object or nested object that you would like to deselect. | ||
* @returns {object|object[]} The object or array of objects without the deselected keys | ||
* @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[]} keys The keys 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 | ||
*/ | ||
deselect: function deselect(subject) { | ||
omit: function omit(subject) { | ||
var keys = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
var allKeys = ob.keys(ob.flatten(subject)); | ||
var subjectKeys = ob.keys(ob.flatten(subject)); | ||
var keysToKeep = []; | ||
@@ -88,3 +107,3 @@ | ||
try { | ||
for (var _iterator = allKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
for (var _iterator = subjectKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var subjectKey = _step.value; | ||
@@ -94,27 +113,33 @@ | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
if ((0, _typeOf2.default)(keys) === 'array') { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = keys[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var keyToRemove = _step2.value; | ||
try { | ||
for (var _iterator2 = keys[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var keyToRemove = _step2.value; | ||
if (subjectKey === keyToRemove) { | ||
keepKey = false; | ||
if (subjectKey === keyToRemove) { | ||
keepKey = false; | ||
} | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} else if ((0, _typeOf2.default)(keys) === 'string') { | ||
if (subjectKey === keys) { | ||
keepKey = false; | ||
} | ||
} | ||
@@ -141,3 +166,3 @@ | ||
return ob.select(subject, keysToKeep); | ||
return ob.pick(subject, keysToKeep); | ||
}, | ||
@@ -148,2 +173,3 @@ /** | ||
* @example | ||
* | ||
* let x = { | ||
@@ -153,10 +179,39 @@ * 'a.b.c': 1, | ||
* 'a.b.d[].0': 2, | ||
* 'a.b.d[].1': 3', | ||
* 'a.b.d[].1': 3, | ||
* } | ||
* | ||
* ob.expand(x) | ||
* // → {a: {b: {c: 1, d: [2,3]}}} | ||
* // → { | ||
* // 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|object[]} The expanded object or array of objects. | ||
* @returns {object|any[]} The expanded object or array of objects. | ||
*/ | ||
@@ -169,78 +224,141 @@ expand: function expand(subject) { | ||
// Determine if an array is represented by the flattened object | ||
var rootObjectPresent = true; | ||
if (depth === 1) { | ||
rootObjectPresent = false; | ||
for (var key in subject) { | ||
var rootArrayPresent = key.match(/^\d/ig); | ||
var keyChains = ob.keys(subject); | ||
rootObjectPresent = rootObjectPresent || !rootArrayPresent; | ||
var isArray = false; | ||
if (true) { | ||
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 i = _step3.value; | ||
if (i.startsWith('[]')) { | ||
isArray = true; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
} | ||
if (rootObjectPresent === false && depth === 1) { | ||
// if a top level array, things need to be handled just a little bit differently | ||
if (isArray) { | ||
res = []; | ||
for (var key in subject) { | ||
res.push(subject[key]); | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
try { | ||
for (var _iterator4 = keyChains[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var keyChain = _step4.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) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
} else { | ||
var keyChains = ob.keys(subject); | ||
} else if (keyChains.length === 1) { | ||
// When the object is just {'example.example': y} | ||
// One key and one value | ||
if (keyChains.length === 1) { | ||
var tmp = {}; | ||
var keyChain = keyChains[0]; // something like 'first.another.another' | ||
var value = subject[keyChain]; | ||
var count = undefined; | ||
var tmp = {}; | ||
var keyChain = keyChains[0]; // something like 'first.another.another' | ||
var value = subject[keyChain]; | ||
var count = undefined; | ||
res = tmp; // Poining to tmp so that we have a place holder before nesting | ||
count = 1; | ||
var keys = keyChain.split('.'); | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
res = tmp; // Pointing to tmp so that we have a place holder before nesting | ||
count = 1; | ||
var keys = keyChain.split('.'); | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
try { | ||
for (var _iterator3 = keys[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var key = _step3.value; | ||
try { | ||
for (var _iterator5 = keys[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var key = _step5.value; | ||
if (count === keys.length) { | ||
tmp[key] = value; | ||
if (count === keys.length) { | ||
tmp[key] = value; | ||
} else { | ||
var _isArray = (0, _stringContains2.default)(key, '[]'); | ||
if (_isArray) { | ||
key = key.replace('[]', ''); | ||
tmp[key] = []; | ||
} else { | ||
var isArray = (0, _stringContains2.default)(key, '[]'); | ||
if (isArray) { | ||
key = key.replace('[]', ''); | ||
tmp[key] = []; | ||
} else { | ||
tmp[key] = {}; | ||
} | ||
tmp[key] = {}; | ||
} | ||
tmp = tmp[key]; | ||
} | ||
count++; | ||
tmp = tmp[key]; | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
count++; | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
} | ||
} 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 = (0, _deepmerge2.default)(res, ob.expand(tmp, ++depth)); | ||
} | ||
} | ||
} 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)); | ||
} | ||
} | ||
@@ -264,9 +382,33 @@ return res; | ||
* // → { | ||
* 'a.b.c': 1, | ||
* 'a.b.d': [2,3] | ||
* 'a.b.d[].0': 2, | ||
* 'a.b.d[].1': 3', | ||
* } | ||
* // 'a.b.c': 1, | ||
* // 'a.b.d': [2,3] | ||
* // 'a.b.d[].0': 2, | ||
* // 'a.b.d[].1': 3', | ||
* //} | ||
* | ||
* @param {object|object[]} subject The object or array of objects to perform the flattening on | ||
* @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 | ||
@@ -278,56 +420,30 @@ */ | ||
var res = undefined; | ||
var res = {}; | ||
if ((0, _typeOf2.default)(subject) === 'array' && depth === 1) { | ||
res = []; | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
try { | ||
for (var _iterator4 = subject[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var i = _step4.value; | ||
res = res.concat(ob.flatten(i, prefix, ++depth)); | ||
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; | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
// 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; | ||
} | ||
} | ||
return res; | ||
} else { | ||
res = {}; | ||
if ((0, _typeOf2.default)(subject[i]) === 'array') { | ||
tmpPrefix = tmpPrefix + '[]'; | ||
} | ||
if ((0, _typeOf2.default)(subject) === 'object' || (0, _typeOf2.default)(subject) === 'array') { | ||
res[tmpPrefix] = subject[i]; | ||
for (var i in subject) { | ||
var tmpPrefix = undefined; | ||
if (prefix === '') { | ||
tmpPrefix = '' + i; | ||
} else { | ||
tmpPrefix = prefix + '.' + i; | ||
} | ||
if ((0, _typeOf2.default)(subject[i]) === 'array') { | ||
tmpPrefix = tmpPrefix + '[]'; | ||
} | ||
res[tmpPrefix] = subject[i]; | ||
res = (0, _deepmerge2.default)(res, ob.flatten(subject[i], tmpPrefix, ++depth)); | ||
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; | ||
@@ -354,16 +470,19 @@ }, | ||
* | ||
* @param {object|object[]} subject The object or array of objects whose keys you wish to retrieve. | ||
* @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; | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator5 = subject[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var i = _step5.value; | ||
for (var _iterator6 = subject[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var i = _step6.value; | ||
@@ -373,17 +492,15 @@ keys = keys.concat(ob.keys(i)); | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return) { | ||
_iterator5.return(); | ||
if (!_iteratorNormalCompletion6 && _iterator6.return) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
} | ||
return (0, _uniques2.default)(keys); | ||
} else if ((0, _typeOf2.default)(subject) === 'object') { | ||
@@ -393,6 +510,9 @@ for (var k in subject) { | ||
}; | ||
return (0, _uniques2.default)(keys); | ||
} else { | ||
keys.push(subject); | ||
} | ||
if (unique) { | ||
return (0, _uniques2.default)(keys); | ||
} else { | ||
return keys; | ||
} | ||
@@ -413,57 +533,101 @@ }, | ||
* | ||
* ob.removeUndefs(x) | ||
* ob.filter(x) | ||
* // → { | ||
* b: { | ||
* d: 2, | ||
* }, | ||
* e: [1, 2] | ||
* } | ||
* // b: { | ||
* // d: 2, | ||
* // }, | ||
* // e: [1, 2] | ||
* //} | ||
* | ||
* | ||
* @param {object|object[]} subject The object or array of objects you would like to remove undefined values from. | ||
* @returns {object|object[]} The object or array of objects without any undefined values | ||
* @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 | ||
*/ | ||
removeUndefs: function removeUndefs(subject) { | ||
// Make sure we don't mutate the original object | ||
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 (subject[key] === undefined) {} else { | ||
res.push(ob.removeUndefs(subject[key])); | ||
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 (subject[key] === undefined) { | ||
if (validate(subject[key]) === true) { | ||
subject[key] = ob.filter(subject[key], validate); | ||
} else { | ||
delete subject[key]; | ||
} else { | ||
subject[key] = ob.removeUndefs(subject[key]); | ||
} | ||
} | ||
return subject; | ||
} else { | ||
return subject; | ||
} | ||
return res; | ||
return subject; | ||
}, | ||
/** | ||
* 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 selected keys. | ||
* Merges the enumerable attributes of two objects deeply. | ||
* | ||
* @example <caption>Basic usage.</caption> | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* a: {b: 1}, | ||
* } | ||
* | ||
* ob.select(x, ['a','b']); | ||
* // → {a: 1, b: 2} | ||
* let y = { | ||
* a: {c: 1}, | ||
* } | ||
* | ||
* @example <caption>Advanced usage.</caption> | ||
* 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 = { | ||
@@ -475,10 +639,10 @@ * c: 3, | ||
* | ||
* ob.select(x, ['d.e','d.f[].0','g[].1']); | ||
* ob.pick(x, ['d.e','d.f[].0','g[].1']); | ||
* // → {d: {e: 4, f: [5]}, g: [8]} | ||
* | ||
* @param {object|object[]} subject The object or array of objects to perform the select operation on | ||
* @param {string[]} keys The keys you would like to select | ||
* @returns {object|object[]} The object or array of objects with only the selected keys. | ||
* @param {object|any[]} subject The object or array of objects to perform the pick operation on | ||
* @param {string|string[]} keys The keys you would like to pick | ||
* @returns {object|any[]} The object or array of objects with only the picked keys. | ||
*/ | ||
select: function select(subject) { | ||
pick: function pick(subject) { | ||
var keys = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
@@ -488,35 +652,8 @@ | ||
if ((0, _typeOf2.default)(subject) === 'array') { | ||
resp = []; | ||
resp = {}; | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
var flat = ob.flatten(subject); | ||
try { | ||
for (var _iterator6 = subject[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var i = _step6.value; | ||
resp = resp.concat(ob.select(i, keys)); | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
} | ||
} else { | ||
resp = {}; | ||
var flat = ob.flatten(subject); | ||
for (var actualKey in flat) { | ||
for (var actualKey in flat) { | ||
if ((0, _typeOf2.default)(keys) === 'array') { | ||
var _iteratorNormalCompletion7 = true; | ||
@@ -548,7 +685,10 @@ var _didIteratorError7 = false; | ||
} | ||
} else if ((0, _typeOf2.default)(keys) === 'string') { | ||
if (actualKey === keys) { | ||
resp[actualKey] = flat[actualKey]; | ||
} | ||
} | ||
resp = ob.expand(resp); | ||
} | ||
return resp; | ||
return ob.expand(resp); | ||
}, | ||
@@ -579,6 +719,9 @@ /** | ||
* | ||
* @param {object|object[]} subject The object or array of objects to get the values of | ||
* @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 = []; | ||
@@ -611,4 +754,2 @@ | ||
} | ||
return (0, _uniques2.default)(values); | ||
} else if ((0, _typeOf2.default)(subject) === 'object') { | ||
@@ -618,6 +759,9 @@ for (var k in subject) { | ||
}; | ||
return (0, _uniques2.default)(values); | ||
} else { | ||
values.push(subject); | ||
} | ||
if (unique) { | ||
return (0, _uniques2.default)(values); | ||
} else { | ||
return values; | ||
} | ||
@@ -624,0 +768,0 @@ } |
{ | ||
"name": "objob", | ||
"version": "2.3.0", | ||
"version": "2.4.0", | ||
"description": "A tool for controlling and manipulating javascript object fields and output.", | ||
@@ -23,3 +23,2 @@ "main": "lib/objob.js", | ||
"dependencies": { | ||
"deepmerge": "^0.2.10", | ||
"string-contains": "^0.1.0", | ||
@@ -26,0 +25,0 @@ "type-of": "^2.0.1", |
# Ob Job | ||
A library of deep recursive object operations. Many functions will be similar to lodash except deeper. | ||
A library of deep/recursive utilities for nested objects. Many functions will be similar to the object functions in lodash but will be deep/recursive versions. | ||
@@ -15,3 +15,7 @@ ```bash | ||
## Special Thanks | ||
* [Kyle Matthews - Merge](https://github.com/KyleAMathews/deepmerge) | ||
## License | ||
MIT |
460
src/objob.js
@@ -6,3 +6,2 @@ 'use strict'; | ||
import contains from 'string-contains'; | ||
import merge from 'deepmerge'; | ||
import { makeFlattenedShallow } from './functions'; | ||
@@ -21,28 +20,27 @@ | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* d: {f: 4} | ||
* } | ||
* | ||
* y = ob.clone(x) | ||
* | ||
* (x.a === y.a && x.d.f === y.d.f) | ||
* // → true | ||
* | ||
* y === x | ||
* // → false | ||
* | ||
* @param {object|object[]} subject The object or array to clone. | ||
* @returns {object|object[]} The cloned object or arraay | ||
* @param {object|any[]} subject The object or array to clone. | ||
* @returns {object|any[]} The cloned object or arraay | ||
*/ | ||
clone: function(subject){ | ||
return ob.expand(ob.flatten(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 <caption>Basic usage.</caption> | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* } | ||
* | ||
* ob.deselect(x, ['a','b']); | ||
* // → {c: 3} | ||
* @example <caption>Advanced usage.</caption> | ||
* @example | ||
* let x = { | ||
@@ -54,18 +52,46 @@ * c: 3, | ||
* | ||
* ob.deselect(x, ['d.e','d.f[].0','g[].1']); | ||
* // → {c: 3, d: {f:[6]}, g:[7]} | ||
* ob.omit(x, ['d.e','d.f[].0','g[].1']); | ||
* // → { | ||
* // c:3, | ||
* // d: { | ||
* // f: [6] | ||
* // }, | ||
* // g:[7] | ||
* //} | ||
* | ||
* @param {object|object[]} subject The object or array to perform the deselect operation on. | ||
* @param {string[]} keys The keys of the object or nested object that you would like to deselect. | ||
* @returns {object|object[]} The object or array of objects without the deselected keys | ||
* @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[]} keys The keys 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 | ||
*/ | ||
deselect: function(subject, keys = []){ | ||
let allKeys = ob.keys(ob.flatten(subject)); | ||
omit: function(subject, keys = []){ | ||
let subjectKeys = ob.keys(ob.flatten(subject)); | ||
let keysToKeep = []; | ||
for( let subjectKey of allKeys ) { | ||
for( let subjectKey of subjectKeys ) { | ||
let keepKey = true; | ||
for( let keyToRemove of keys ){ | ||
if(subjectKey === keyToRemove){ | ||
if(type(keys) === 'array') { | ||
for( let keyToRemove of keys ){ | ||
if(subjectKey === keyToRemove){ | ||
keepKey = false; | ||
} | ||
} | ||
} else if(type(keys) === 'string') { | ||
if(subjectKey === keys){ | ||
keepKey = false; | ||
@@ -78,5 +104,6 @@ } | ||
} | ||
} | ||
return ob.select(subject, keysToKeep); | ||
return ob.pick(subject, keysToKeep); | ||
}, | ||
@@ -87,2 +114,3 @@ /** | ||
* @example | ||
* | ||
* let x = { | ||
@@ -92,10 +120,39 @@ * 'a.b.c': 1, | ||
* 'a.b.d[].0': 2, | ||
* 'a.b.d[].1': 3', | ||
* 'a.b.d[].1': 3, | ||
* } | ||
* | ||
* ob.expand(x) | ||
* // → {a: {b: {c: 1, d: [2,3]}}} | ||
* // → { | ||
* // 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|object[]} The expanded object or array of objects. | ||
* @returns {object|any[]} The expanded object or array of objects. | ||
*/ | ||
@@ -106,58 +163,79 @@ expand: function(subject, depth = 1){ | ||
// Determine if an array is represented by the flattened object | ||
let rootObjectPresent = true; | ||
if(depth === 1) { | ||
rootObjectPresent = false; | ||
for(let key in subject) { | ||
let rootArrayPresent = key.match(/^\d/ig); | ||
let keyChains = ob.keys(subject); | ||
rootObjectPresent = (rootObjectPresent || !rootArrayPresent); | ||
let isArray = false; | ||
if(true) { | ||
for(let i of keyChains) { | ||
if(i.startsWith('[]')) { | ||
isArray = true; | ||
} | ||
} | ||
} | ||
if(rootObjectPresent === false && depth === 1) { | ||
// if a top level array, things need to be handled just a little bit differently | ||
if(isArray) { | ||
res = []; | ||
for(let key in subject) { | ||
res.push(subject[key]); | ||
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 { | ||
let keyChains = ob.keys(subject); | ||
} else if(keyChains.length === 1) { | ||
// When the object is just {'example.example': y} | ||
// One key and one value | ||
if(keyChains.length === 1) { | ||
let tmp = {}; | ||
let keyChain = keyChains[0]; // something like 'first.another.another' | ||
let value = subject[keyChain]; | ||
let count; | ||
let tmp = {}; | ||
let keyChain = keyChains[0]; // something like 'first.another.another' | ||
let value = subject[keyChain]; | ||
let count; | ||
res = tmp; // Poining 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] = value; | ||
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] = value; | ||
} else { | ||
let isArray = contains(key, '[]'); | ||
if(isArray) { | ||
key = key.replace('[]',''); | ||
tmp[key] = []; | ||
} else { | ||
let isArray = contains(key, '[]'); | ||
if(isArray) { | ||
key = key.replace('[]',''); | ||
tmp[key] = []; | ||
} else { | ||
tmp[key] = {}; | ||
} | ||
tmp = tmp[key]; | ||
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 = merge(res, ob.expand(tmp, ++depth)); | ||
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)); | ||
} | ||
} | ||
@@ -181,45 +259,64 @@ return res; | ||
* // → { | ||
* 'a.b.c': 1, | ||
* 'a.b.d': [2,3] | ||
* 'a.b.d[].0': 2, | ||
* 'a.b.d[].1': 3', | ||
* } | ||
* // 'a.b.c': 1, | ||
* // 'a.b.d': [2,3] | ||
* // 'a.b.d[].0': 2, | ||
* // 'a.b.d[].1': 3', | ||
* //} | ||
* | ||
* @param {object|object[]} subject The object or array of objects to perform the flattening on | ||
* @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; | ||
let res = {}; | ||
if(type(subject) === 'array' && depth === 1) { | ||
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}`; | ||
} | ||
for(let i of subject){ | ||
res = res.concat(ob.flatten(i, prefix, ++depth)); | ||
} | ||
if(type(subject[i]) === 'array') { | ||
tmpPrefix = tmpPrefix + '[]'; | ||
} | ||
return res; | ||
} else { | ||
res = {}; | ||
res[tmpPrefix] = subject[i]; | ||
if(type(subject) === 'object' || type(subject) === 'array'){ | ||
for(let i in subject) { | ||
let tmpPrefix; | ||
if(prefix === '') { | ||
tmpPrefix = `${i}`; | ||
} else { | ||
tmpPrefix = `${prefix}.${i}`; | ||
} | ||
if(type(subject[i]) === 'array') { | ||
tmpPrefix = tmpPrefix + '[]'; | ||
} | ||
res[tmpPrefix] = subject[i]; | ||
res = merge(res, ob.flatten(subject[i],tmpPrefix, ++depth)); | ||
if(type(subject[i]) === 'array' || type(subject[i]) === 'object') { | ||
res = ob.merge(res, ob.flatten(subject[i],tmpPrefix, depth+1)); | ||
} | ||
} | ||
} | ||
return res; | ||
@@ -246,6 +343,7 @@ }, | ||
* | ||
* @param {object|object[]} subject The object or array of objects whose keys you wish to retrieve. | ||
* @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) { | ||
keys:function(subject, unique=true) { | ||
let keys = []; | ||
@@ -258,3 +356,2 @@ | ||
return uniques(keys); | ||
} else if(type(subject) === 'object') { | ||
@@ -264,6 +361,9 @@ for(let k in subject) { | ||
}; | ||
return uniques(keys); | ||
} else { | ||
keys.push(subject); | ||
} | ||
if(unique) { | ||
return uniques(keys); | ||
} else { | ||
return keys; | ||
} | ||
@@ -284,58 +384,102 @@ }, | ||
* | ||
* ob.removeUndefs(x) | ||
* ob.filter(x) | ||
* // → { | ||
* b: { | ||
* d: 2, | ||
* }, | ||
* e: [1, 2] | ||
* } | ||
* // b: { | ||
* // d: 2, | ||
* // }, | ||
* // e: [1, 2] | ||
* //} | ||
* | ||
* | ||
* @param {object|object[]} subject The object or array of objects you would like to remove undefined values from. | ||
* @returns {object|object[]} The object or array of objects without any undefined values | ||
* @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 | ||
*/ | ||
removeUndefs: (subject) => { | ||
// Make sure we don't mutate the original object | ||
filter: (subject, validate) => { | ||
subject = ob.clone(subject); | ||
let res; | ||
if(type(subject) === 'array') { | ||
res = []; | ||
for(let key in subject) { | ||
if(subject[key] === undefined) { | ||
} else { | ||
res.push(ob.removeUndefs(subject[key])); | ||
if(validate(subject[key])) { | ||
res.push(ob.filter(subject[key], validate)); | ||
} | ||
} | ||
subject = res; | ||
} else if(type(subject) === 'object') { | ||
for(let key in subject) { | ||
if(subject[key] === undefined) { | ||
if(validate(subject[key]) === true ) { | ||
subject[key] = ob.filter(subject[key], validate); | ||
} else { | ||
delete subject[key]; | ||
} else { | ||
subject[key] = ob.removeUndefs(subject[key]); | ||
} | ||
} | ||
return subject; | ||
} else { | ||
return subject; | ||
} | ||
return res; | ||
return subject; | ||
}, | ||
/** | ||
* 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 selected keys. | ||
* Merges the enumerable attributes of two objects deeply. | ||
* | ||
* @example <caption>Basic usage.</caption> | ||
* @example | ||
* let x = { | ||
* a: 1, | ||
* b: 2, | ||
* c: 3, | ||
* a: {b: 1}, | ||
* } | ||
* | ||
* ob.select(x, ['a','b']); | ||
* // → {a: 1, b: 2} | ||
* let y = { | ||
* a: {c: 1}, | ||
* } | ||
* | ||
* @example <caption>Advanced usage.</caption> | ||
* 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 = { | ||
@@ -347,24 +491,18 @@ * c: 3, | ||
* | ||
* ob.select(x, ['d.e','d.f[].0','g[].1']); | ||
* ob.pick(x, ['d.e','d.f[].0','g[].1']); | ||
* // → {d: {e: 4, f: [5]}, g: [8]} | ||
* | ||
* @param {object|object[]} subject The object or array of objects to perform the select operation on | ||
* @param {string[]} keys The keys you would like to select | ||
* @returns {object|object[]} The object or array of objects with only the selected keys. | ||
* @param {object|any[]} subject The object or array of objects to perform the pick operation on | ||
* @param {string|string[]} keys The keys you would like to pick | ||
* @returns {object|any[]} The object or array of objects with only the picked keys. | ||
*/ | ||
select: (subject, keys = []) => { | ||
pick: (subject, keys = []) => { | ||
let resp; | ||
if(type(subject) === 'array') { | ||
resp = []; | ||
resp = {}; | ||
for(let i of subject){ | ||
resp = resp.concat(ob.select(i, keys)); | ||
} | ||
} else { | ||
resp = {}; | ||
let flat = ob.flatten(subject); | ||
let flat = ob.flatten(subject); | ||
for (let actualKey in flat){ | ||
for (let actualKey in flat){ | ||
if(type(keys) === 'array') { | ||
for (let desiredKey of keys){ | ||
@@ -375,7 +513,10 @@ if(actualKey === desiredKey) { | ||
} | ||
} else if(type(keys) === 'string') { | ||
if(actualKey === keys) { | ||
resp[actualKey] = flat[actualKey]; | ||
} | ||
} | ||
resp = ob.expand(resp); | ||
} | ||
return resp; | ||
return ob.expand(resp); | ||
}, | ||
@@ -406,6 +547,7 @@ /** | ||
* | ||
* @param {object|object[]} subject The object or array of objects to get the values of | ||
* @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) => { | ||
values:(subject, unique=true) => { | ||
let values = []; | ||
@@ -418,3 +560,2 @@ | ||
return uniques(values); | ||
} else if(type(subject) === 'object') { | ||
@@ -424,6 +565,9 @@ for(let k in subject) { | ||
}; | ||
return uniques(values); | ||
} else { | ||
values.push(subject); | ||
} | ||
if(unique) { | ||
return uniques(values); | ||
} else { | ||
return values; | ||
} | ||
@@ -430,0 +574,0 @@ }, |
127
tests.js
@@ -7,4 +7,4 @@ /* eslint max-nested-callbacks: 0*/ | ||
describe('Objob', () => { | ||
let ob1, ob2, ob3, ob4; | ||
let obArr1; | ||
let ob1, ob2, ob3, ob4, ob5; | ||
let arr2, arr1, arr3, arr4; | ||
@@ -48,4 +48,18 @@ before((done) => { | ||
obArr1 = [ob1, ob2]; | ||
ob5 = { | ||
name: undefined, | ||
feet: 5, | ||
body: { | ||
feet: { | ||
toes: [undefined,2,10], | ||
}, | ||
hair: undefined, | ||
}, | ||
}; | ||
arr1 = [3, 1]; | ||
arr2 = [ob1, ob2]; | ||
arr3 = [arr2, arr2]; | ||
arr4 = [arr2, arr1]; | ||
done(); | ||
@@ -60,3 +74,3 @@ }); | ||
it('should return all keys for an array', (done) => { | ||
expect(ob.keys(obArr1)).to.include.members(['name','age', 'weight','feet']); | ||
expect(ob.keys(arr2)).to.include.members(['name','age', 'weight','feet']); | ||
done(); | ||
@@ -72,3 +86,3 @@ }); | ||
it('should return all values for an array', (done) => { | ||
expect(ob.values(obArr1)).to.include.members([ob1.name, ob1.age, ob1.weight, ob2.feet]); | ||
expect(ob.values(arr2)).to.include.members([ob1.name, ob1.age, ob1.weight, ob2.feet]); | ||
done(); | ||
@@ -78,7 +92,7 @@ }); | ||
describe('select', () => { | ||
it('should return the object only select the given keys', (done) => { | ||
expect(ob.select(ob1, ['name'])).to.deep.equal({name: ob1.name}); | ||
expect(ob.select(ob2,['name', 'age'])).to.deep.equal({name: ob2.name, age: ob2.age}); | ||
expect(ob.select(ob3, ['eyes[].0.location'])).to.deep.equal({ | ||
describe('pick', () => { | ||
it('should return the object only pick the given keys', (done) => { | ||
expect(ob.pick(ob1, ['name'])).to.deep.equal({name: ob1.name}); | ||
expect(ob.pick(ob2,['name', 'age'])).to.deep.equal({name: ob2.name, age: ob2.age}); | ||
expect(ob.pick(ob3, ['eyes[].0.location'])).to.deep.equal({ | ||
eyes: [{location: 'left'}], | ||
@@ -89,9 +103,9 @@ }); | ||
it('should return the object only select the given keys using nested object', (done) => { | ||
expect(ob.select(ob3, ['body.feet'])).to.deep.equal({body: {feet: ob3.body.feet}}); | ||
it('should return the object only pick the given keys using nested object', (done) => { | ||
expect(ob.pick(ob3, ['body.feet'])).to.deep.equal({body: {feet: ob3.body.feet}}); | ||
done(); | ||
}); | ||
it('should return the array only select the given keys using nested object', (done) => { | ||
expect(ob.select([ob3, ob3], ['body.feet'])) | ||
it('should return the array only pick the given keys using nested object', (done) => { | ||
expect(ob.pick([ob3, ob3], ['[]0.body.feet', '[]1.body.feet'])) | ||
.to.deep.equal([{body: {feet: ob3.body.feet}}, {body: {feet: ob3.body.feet}}]); | ||
@@ -101,4 +115,4 @@ done(); | ||
it('should return an array of objects only select the given keys', (done) => { | ||
expect(ob.select(obArr1, ['name'])).to.deep.equal([{name: ob1.name},{name: ob2.name}]); | ||
it('should return an array of objects with the given keys', (done) => { | ||
expect(ob.pick(arr2, ['[]0.name', '[]1.name'])).to.deep.equal([{name: ob1.name},{name: ob2.name}]); | ||
done(); | ||
@@ -108,9 +122,8 @@ }); | ||
describe('deselect', () => { | ||
it('should return the object only deselect the given keys', (done) => { | ||
expect(ob.deselect(ob1, ['name'])).to.deep.equal({age: ob1.age, weight: ob1.weight}); | ||
expect(ob.deselect(ob2, ['name', 'age'])).to.deep.equal({weight: ob2.weight, feet: ob2.feet}); | ||
done(); | ||
describe('omit', () => { | ||
it('should return the object only omit the given keys', (done) => { | ||
expect(ob.omit(ob1, ['name'])).to.deep.equal({age: ob1.age, weight: ob1.weight}); | ||
expect(ob.omit(ob2, ['name', 'age'])).to.deep.equal({weight: ob2.weight, feet: ob2.feet}); | ||
expect(ob.deselect(ob3, ['eyes[].0.location'])).to.deep.equal({ | ||
expect(ob.omit(ob3, ['eyes[].0.location'])).to.deep.equal({ | ||
name: ob3.name, | ||
@@ -125,6 +138,7 @@ feet: ob3.feet, | ||
}); | ||
done(); | ||
}); | ||
it('should return an array of objects only deselect the given keys', (done) => { | ||
expect(ob.deselect(obArr1, ['name', 'weight', 'feet'])) | ||
it('should return an array of objects only omit the given keys', (done) => { | ||
expect(ob.omit(arr2, ['[]0.name', '[]0.weight', '[]0.feet', '[]1.name', '[]1.weight', '[]1.feet'])) | ||
.to.deep.equal([{age: ob1.age}, {age: ob2.age}]); | ||
@@ -157,2 +171,40 @@ | ||
expect(ob.flatten(ob1)).to.deep.equal(ob1); | ||
expect(ob.flatten(arr3)).to.deep.equal({ | ||
'[]0[]': [ { name: 'Bob', age: 22, weight: 170 },{ name: 'Bob', feet: 5, age: 100, weight: 170 } ], | ||
'[]0[].0': { name: 'Bob', age: 22, weight: 170 }, | ||
'[]0[].0.name': 'Bob', | ||
'[]0[].0.age': 22, | ||
'[]0[].0.weight': 170, | ||
'[]0[].1': { name: 'Bob', feet: 5, age: 100, weight: 170 }, | ||
'[]0[].1.name': 'Bob', | ||
'[]0[].1.feet': 5, | ||
'[]0[].1.age': 100, | ||
'[]0[].1.weight': 170, | ||
'[]1[]':[ { name: 'Bob', age: 22, weight: 170 }, { name: 'Bob', feet: 5, age: 100, weight: 170 } ], | ||
'[]1[].0': { name: 'Bob', age: 22, weight: 170 }, | ||
'[]1[].0.name': 'Bob', | ||
'[]1[].0.age': 22, | ||
'[]1[].0.weight': 170, | ||
'[]1[].1': { name: 'Bob', feet: 5, age: 100, weight: 170 }, | ||
'[]1[].1.name': 'Bob', | ||
'[]1[].1.feet': 5, | ||
'[]1[].1.age': 100, | ||
'[]1[].1.weight': 170, | ||
}); | ||
expect(ob.flatten(arr4)).to.deep.equal({ | ||
'[]0[]': [ { name: 'Bob', age: 22, weight: 170 }, { name: 'Bob', feet: 5, age: 100, weight: 170 } ], | ||
'[]0[].0': { name: 'Bob', age: 22, weight: 170 }, | ||
'[]0[].0.name': 'Bob', | ||
'[]0[].0.age': 22, | ||
'[]0[].0.weight': 170, | ||
'[]0[].1': { name: 'Bob', feet: 5, age: 100, weight: 170 }, | ||
'[]0[].1.name': 'Bob', | ||
'[]0[].1.feet': 5, | ||
'[]0[].1.age': 100, | ||
'[]0[].1.weight': 170, | ||
'[]1[]': [ 3, 1 ], | ||
'[]1[].0': 3, | ||
'[]1[].1': 1, | ||
}); | ||
done(); | ||
@@ -167,3 +219,7 @@ }); | ||
expect(ob.expand(ob.flatten(ob4))).to.deep.equal(ob4); | ||
expect(ob.expand(ob.flatten(obArr1))).to.deep.equal(obArr1); | ||
expect(ob.expand(ob.flatten(arr2))).to.deep.equal(arr2); | ||
expect(ob.expand(ob.flatten(arr1))).to.deep.equal(arr1); | ||
expect(ob.expand(ob.flatten(arr3))).to.deep.equal(arr3); | ||
expect(ob.expand(ob.flatten(arr4))).to.deep.equal(arr4); | ||
expect(ob.expand(ob.flatten({tmp: arr4}))).to.deep.equal({tmp: arr4}); | ||
done(); | ||
@@ -173,9 +229,24 @@ }); | ||
describe('clone deep', () => { | ||
describe('clone', () => { | ||
it('should return a clone of the object', (done) => { | ||
expect(ob.cloneDeep(ob3)).to.deep.equal(ob3); | ||
expect(ob.cloneDeep(ob3)).to.not.equal(ob3); | ||
expect(ob.clone(ob3)).to.deep.equal(ob3); | ||
expect(ob.clone(ob3)).to.not.equal(ob3); | ||
done(); | ||
}); | ||
}); | ||
describe('filter', () => { | ||
it('should return an object with undefineds filtered out', (done) => { | ||
expect(ob.filter(ob5, (x) => x !== undefined)).to.deep.equal({ | ||
feet: ob5.feet, | ||
body: { | ||
feet: { | ||
toes: [2,10], | ||
}, | ||
}, | ||
}); | ||
done(); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
640925
3
2657
21
- Removeddeepmerge@^0.2.10
- Removeddeepmerge@0.2.10(transitive)