From f132c0024fccd4a2b4d6fbd18df37d7b7996312e Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Fri, 11 Dec 2015 01:39:33 -0800 Subject: [PATCH] Make fp versions of `set` and `setWith` immutable. --- lib/fp/base.js | 20 ++++++++++++++++++++ lib/fp/mapping.js | 11 ++++++++--- lib/fp/util.js | 1 + test/test-fp.js | 17 +++++++++++++++-- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/fp/base.js b/lib/fp/base.js index 1cf8a83d7..d08b9d368 100644 --- a/lib/fp/base.js +++ b/lib/fp/base.js @@ -22,6 +22,7 @@ function baseConvert(util, name, func) { var _ = isLib ? func : { 'ary': util.ary, + 'cloneDeep': util.cloneDeep, 'curry': util.curry, 'forEach': util.forEach, 'isFunction': util.isFunction, @@ -31,6 +32,7 @@ function baseConvert(util, name, func) { }; var ary = _.ary, + cloneDeep = _.cloneDeep, curry = _.curry, each = _.forEach, isFunction = _.isFunction, @@ -90,6 +92,21 @@ function baseConvert(util, name, func) { }; }; + var immutSetWrap = function(func) { + return function() { + var index = -1, + length = arguments.length, + args = Array(length); + + while (++index < length) { + args[index] = arguments[index]; + } + args[0] = cloneDeep(args[0]); + func.apply(undefined, args); + return args[0]; + }; + }; + var iterateeAry = function(func, n) { return function() { var length = arguments.length, @@ -160,6 +177,9 @@ function baseConvert(util, name, func) { else if (mutateMap.object[name]) { func = immutObjectWrap(func); } + else if (mutateMap.set[name]) { + func = immutSetWrap(func); + } var result; each(mapping.caps, function(cap) { each(mapping.aryMethodMap[cap], function(otherName) { diff --git a/lib/fp/mapping.js b/lib/fp/mapping.js index 57305f3e7..15b0f2069 100644 --- a/lib/fp/mapping.js +++ b/lib/fp/mapping.js @@ -85,15 +85,15 @@ module.exports = { 'invokeMap,isMatch,lastIndexOf,map,mapKeys,mapValues,matchesProperty,maxBy,' + 'mean,minBy,merge,omit,overArgs,pad,padLeft,padRight,parseInt,partition,' + 'pick,pull,pullAll,pullAt,random,range,rangeRight,rearg,reject,remove,repeat,' + - 'result,sampleSize,set,some,sortBy,sortByOrder,sortedIndexBy,sortedLastIndexBy,' + + 'result,sampleSize,some,sortBy,sortByOrder,sortedIndexBy,sortedLastIndexBy,' + 'sortedUniqBy,startsWith,subtract,sumBy,take,takeRight,takeRightWhile,takeWhile,' + 'throttle,times,truncate,union,uniqBy,without,wrap,xor,zip,zipObject').split(','), 3: ( 'assignWith,clamp,differenceBy,extendWith,getOr,inRange,intersectionBy,' + 'isEqualWith,isMatchWith,mergeWith,omitBy,pickBy,pullAllBy,reduce,' + - 'reduceRight,slice,transform,unionBy,xorBy,zipWith').split(','), + 'reduceRight,set,slice,transform,unionBy,xorBy,zipWith').split(','), 4: - ['fill'] + ['fill', 'setWith'] }, /** Used to map ary to rearg configs by method ary. */ @@ -108,6 +108,7 @@ module.exports = { 'clamp': [2, 0, 1], 'reduce': [2, 0, 1], 'reduceRight': [2, 0, 1], + 'setWith': [3, 2, 1, 0], 'slice': [2, 0, 1], 'transform': [2, 0, 1] }, @@ -142,6 +143,10 @@ module.exports = { 'extendWith': true, 'merge': true, 'mergeWith': true + }, + 'set': { + 'set': true, + 'setWith': true } }, diff --git a/lib/fp/util.js b/lib/fp/util.js index 341b9de33..472553033 100644 --- a/lib/fp/util.js +++ b/lib/fp/util.js @@ -1,5 +1,6 @@ module.exports = { 'ary': require('lodash/function/ary'), + 'cloneDeep': require('lodash/lang/cloneDeep'), 'curry': require('lodash/function/curry'), 'forEach': require('lodash/internal/arrayEach'), 'isFunction': require('lodash/lang/isFunction'), diff --git a/test/test-fp.js b/test/test-fp.js index fb5f1bfe2..e178d56cd 100644 --- a/test/test-fp.js +++ b/test/test-fp.js @@ -651,10 +651,11 @@ (function() { var array = [1, 2, 3], - object = { 'a': 1 }; + object = { 'a': 1 }, + deepObject = { 'a': { 'b': 2, 'c': 3 } }; QUnit.test('should not mutate values', function(assert) { - assert.expect(28); + assert.expect(32); function Foo() {} Foo.prototype = { 'b': 2 }; @@ -753,6 +754,18 @@ assert.deepEqual(value, array, 'fp.reverse'); assert.deepEqual(actual, [3, 2, 1], 'fp.reverse'); + + value = _.cloneDeep(deepObject); + actual = fp.set(3, 'a.b', value); + + assert.deepEqual(value, deepObject, 'fp.set'); + assert.deepEqual(actual, { 'a': { 'b': 3, 'c': 3 } }, 'fp.set'); + + value = _.cloneDeep(deepObject); + actual = fp.setWith(Object, 4, 'd.e', value); + + assert.deepEqual(value, deepObject, 'fp.setWith'); + assert.deepEqual(actual, { 'a': { 'b': 2, 'c': 3 }, 'd': { 'e': 4 } }, 'fp.setWith'); }); }());