mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-12 03:47:50 +00:00
Ensure _.set and _.setWith don't assign a value if it's the same as the destination value.
This commit is contained in:
13
lodash.js
13
lodash.js
@@ -2511,18 +2511,15 @@
|
|||||||
while (nested != null && ++index < length) {
|
while (nested != null && ++index < length) {
|
||||||
var key = path[index];
|
var key = path[index];
|
||||||
if (isObject(nested)) {
|
if (isObject(nested)) {
|
||||||
if (index == lastIndex) {
|
var newValue = value;
|
||||||
nested[key] = value;
|
if (index != lastIndex) {
|
||||||
}
|
var oldValue = nested[key];
|
||||||
else {
|
newValue = customizer ? customizer(oldValue, key, nested) : undefined;
|
||||||
var oldValue = nested[key],
|
|
||||||
newValue = customizer ? customizer(oldValue, key, nested) : undefined;
|
|
||||||
|
|
||||||
if (newValue === undefined) {
|
if (newValue === undefined) {
|
||||||
newValue = oldValue == null ? (isIndex(path[index + 1]) ? [] : {}) : oldValue;
|
newValue = oldValue == null ? (isIndex(path[index + 1]) ? [] : {}) : oldValue;
|
||||||
}
|
}
|
||||||
assignValue(nested, key, newValue);
|
|
||||||
}
|
}
|
||||||
|
assignValue(nested, key, newValue);
|
||||||
}
|
}
|
||||||
nested = nested[key];
|
nested = nested[key];
|
||||||
}
|
}
|
||||||
|
|||||||
82
test/test.js
82
test/test.js
@@ -5121,7 +5121,7 @@
|
|||||||
'set': function() { pass = false; }
|
'set': function() { pass = false; }
|
||||||
});
|
});
|
||||||
|
|
||||||
func(object, { 'a': value }, _.identity);
|
func(object, { 'a': value });
|
||||||
ok(pass, value);
|
ok(pass, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -13404,67 +13404,69 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
QUnit.module('lodash.set');
|
QUnit.module('set methods');
|
||||||
|
|
||||||
(function() {
|
_.each(['set', 'setWith'], function(methodName) {
|
||||||
test('should set property values', 4, function() {
|
var func = _[methodName];
|
||||||
|
|
||||||
|
test('`_.' + methodName + '` should set property values', 4, function() {
|
||||||
var object = { 'a': 1 };
|
var object = { 'a': 1 };
|
||||||
|
|
||||||
_.each(['a', ['a']], function(path) {
|
_.each(['a', ['a']], function(path) {
|
||||||
var actual = _.set(object, path, 2);
|
var actual = func(object, path, 2);
|
||||||
strictEqual(actual, object);
|
strictEqual(actual, object);
|
||||||
strictEqual(object.a, 2);
|
strictEqual(object.a, 2);
|
||||||
object.a = 1;
|
object.a = 1;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should set deep property values', 4, function() {
|
test('`_.' + methodName + '` should set deep property values', 4, function() {
|
||||||
var object = { 'a': { 'b': { 'c': 3 } } };
|
var object = { 'a': { 'b': { 'c': 3 } } };
|
||||||
|
|
||||||
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
|
_.each(['a.b.c', ['a', 'b', 'c']], function(path) {
|
||||||
var actual = _.set(object, path, 4);
|
var actual = func(object, path, 4);
|
||||||
strictEqual(actual, object);
|
strictEqual(actual, object);
|
||||||
strictEqual(object.a.b.c, 4);
|
strictEqual(object.a.b.c, 4);
|
||||||
object.a.b.c = 3;
|
object.a.b.c = 3;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should set a key over a path', 4, function() {
|
test('`_.' + methodName + '` should set a key over a path', 4, function() {
|
||||||
var object = { 'a.b.c': 3 };
|
var object = { 'a.b.c': 3 };
|
||||||
|
|
||||||
_.each(['a.b.c', ['a.b.c']], function(path) {
|
_.each(['a.b.c', ['a.b.c']], function(path) {
|
||||||
var actual = _.set(object, path, 4);
|
var actual = func(object, path, 4);
|
||||||
strictEqual(actual, object);
|
strictEqual(actual, object);
|
||||||
deepEqual(object, { 'a.b.c': 4 });
|
deepEqual(object, { 'a.b.c': 4 });
|
||||||
object['a.b.c'] = 3;
|
object['a.b.c'] = 3;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not coerce array paths to strings', 1, function() {
|
test('`_.' + methodName + '` should not coerce array paths to strings', 1, function() {
|
||||||
var object = { 'a,b,c': 3, 'a': { 'b': { 'c': 3 } } };
|
var object = { 'a,b,c': 3, 'a': { 'b': { 'c': 3 } } };
|
||||||
_.set(object, ['a', 'b', 'c'], 4);
|
func(object, ['a', 'b', 'c'], 4);
|
||||||
strictEqual(object.a.b.c, 4);
|
strictEqual(object.a.b.c, 4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should ignore empty brackets', 1, function() {
|
test('`_.' + methodName + '` should ignore empty brackets', 1, function() {
|
||||||
var object = {};
|
var object = {};
|
||||||
_.set(object, 'a[]', 1);
|
func(object, 'a[]', 1);
|
||||||
deepEqual(object, { 'a': 1 });
|
deepEqual(object, { 'a': 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should handle empty paths', 4, function() {
|
test('`_.' + methodName + '` should handle empty paths', 4, function() {
|
||||||
_.each([['', ''], [[], ['']]], function(pair, index) {
|
_.each([['', ''], [[], ['']]], function(pair, index) {
|
||||||
var object = {};
|
var object = {};
|
||||||
|
|
||||||
_.set(object, pair[0], 1);
|
func(object, pair[0], 1);
|
||||||
deepEqual(object, index ? {} : { '': 1 });
|
deepEqual(object, index ? {} : { '': 1 });
|
||||||
|
|
||||||
_.set(object, pair[1], 2);
|
func(object, pair[1], 2);
|
||||||
deepEqual(object, { '': 2 });
|
deepEqual(object, { '': 2 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should handle complex paths', 2, function() {
|
test('`_.' + methodName + '` should handle complex paths', 2, function() {
|
||||||
var object = { 'a': { '1.23': { '["b"]': { 'c': { "['d']": { 'e': { 'f': 6 } } } } } } };
|
var object = { 'a': { '1.23': { '["b"]': { 'c': { "['d']": { 'e': { 'f': 6 } } } } } } };
|
||||||
|
|
||||||
var paths = [
|
var paths = [
|
||||||
@@ -13473,17 +13475,17 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
_.each(paths, function(path) {
|
_.each(paths, function(path) {
|
||||||
_.set(object, path, 7);
|
func(object, path, 7);
|
||||||
strictEqual(object.a[-1.23]['["b"]'].c["['d']"].e.f, 7);
|
strictEqual(object.a[-1.23]['["b"]'].c["['d']"].e.f, 7);
|
||||||
object.a[-1.23]['["b"]'].c["['d']"].e.f = 6;
|
object.a[-1.23]['["b"]'].c["['d']"].e.f = 6;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should create parts of `path` that are missing', 6, function() {
|
test('`_.' + methodName + '` should create parts of `path` that are missing', 6, function() {
|
||||||
var object = {};
|
var object = {};
|
||||||
|
|
||||||
_.each(['a[1].b.c', ['a', '1', 'b', 'c']], function(path) {
|
_.each(['a[1].b.c', ['a', '1', 'b', 'c']], function(path) {
|
||||||
var actual = _.set(object, path, 4);
|
var actual = func(object, path, 4);
|
||||||
strictEqual(actual, object);
|
strictEqual(actual, object);
|
||||||
deepEqual(actual, { 'a': [undefined, { 'b': { 'c': 4 } }] });
|
deepEqual(actual, { 'a': [undefined, { 'b': { 'c': 4 } }] });
|
||||||
ok(!(0 in object.a));
|
ok(!(0 in object.a));
|
||||||
@@ -13491,13 +13493,13 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not error when `object` is nullish', 1, function() {
|
test('`_.' + methodName + '` should not error when `object` is nullish', 1, function() {
|
||||||
var values = [null, undefined],
|
var values = [null, undefined],
|
||||||
expected = [[null, null], [undefined, undefined]];
|
expected = [[null, null], [undefined, undefined]];
|
||||||
|
|
||||||
var actual = _.map(values, function(value) {
|
var actual = _.map(values, function(value) {
|
||||||
try {
|
try {
|
||||||
return [_.set(value, 'a.b', 1), _.set(value, ['a', 'b'], 1)];
|
return [func(value, 'a.b', 1), func(value, ['a', 'b'], 1)];
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
return e.message;
|
return e.message;
|
||||||
}
|
}
|
||||||
@@ -13506,29 +13508,29 @@
|
|||||||
deepEqual(actual, expected);
|
deepEqual(actual, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should follow `path` over non-plain objects', 4, function() {
|
test('`_.' + methodName + '` should follow `path` over non-plain objects', 4, function() {
|
||||||
var object = { 'a': '' },
|
var object = { 'a': '' },
|
||||||
paths = ['constructor.prototype.a', ['constructor', 'prototype', 'a']];
|
paths = ['constructor.prototype.a', ['constructor', 'prototype', 'a']];
|
||||||
|
|
||||||
_.each(paths, function(path) {
|
_.each(paths, function(path) {
|
||||||
_.set(0, path, 1);
|
func(0, path, 1);
|
||||||
strictEqual(0..a, 1);
|
strictEqual(0..a, 1);
|
||||||
delete numberProto.a;
|
delete numberProto.a;
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each(['a.replace.b', ['a', 'replace', 'b']], function(path) {
|
_.each(['a.replace.b', ['a', 'replace', 'b']], function(path) {
|
||||||
_.set(object, path, 1);
|
func(object, path, 1);
|
||||||
strictEqual(stringProto.replace.b, 1);
|
strictEqual(stringProto.replace.b, 1);
|
||||||
delete stringProto.replace.b;
|
delete stringProto.replace.b;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not error on paths over primitive values in strict mode', 2, function() {
|
test('`_.' + methodName + '` should not error on paths over primitive values in strict mode', 2, function() {
|
||||||
numberProto.a = 0;
|
numberProto.a = 0;
|
||||||
|
|
||||||
_.each(['a', 'a.a.a'], function(path) {
|
_.each(['a', 'a.a.a'], function(path) {
|
||||||
try {
|
try {
|
||||||
_.set(0, path, 1);
|
func(0, path, 1);
|
||||||
strictEqual(0..a, 0);
|
strictEqual(0..a, 0);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
ok(false, e.message);
|
ok(false, e.message);
|
||||||
@@ -13539,13 +13541,33 @@
|
|||||||
delete numberProto.a;
|
delete numberProto.a;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not create an array for missing non-index property names that start with numbers', 1, function() {
|
test('`_.' + methodName + '` should not create an array for missing non-index property names that start with numbers', 1, function() {
|
||||||
var object = {};
|
var object = {};
|
||||||
|
|
||||||
_.set(object, ['1a', '2b', '3c'], 1);
|
func(object, ['1a', '2b', '3c'], 1);
|
||||||
deepEqual(object, { '1a': { '2b': { '3c': 1 } } });
|
deepEqual(object, { '1a': { '2b': { '3c': 1 } } });
|
||||||
});
|
});
|
||||||
}());
|
|
||||||
|
test('`_.' + methodName + '` should not assign values that are the same as their destinations', 4, function() {
|
||||||
|
_.each(['a', ['a'], { 'a': 1 }, NaN], function(value) {
|
||||||
|
if (defineProperty) {
|
||||||
|
var object = {},
|
||||||
|
pass = true;
|
||||||
|
|
||||||
|
defineProperty(object, 'a', {
|
||||||
|
'get': _.constant(value),
|
||||||
|
'set': function() { pass = false; }
|
||||||
|
});
|
||||||
|
|
||||||
|
func(object, 'a', value);
|
||||||
|
ok(pass, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
skipTest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user