New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/set #2961
base: master
Are you sure you want to change the base?
Feature/set #2961
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import isNumber from './isNumber.js'; | ||
import isArray from './isArray.js'; | ||
import isObject from './isObject.js'; | ||
|
||
|
||
export default function set (obj, path, value) { | ||
var key = String(path[0]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think calling |
||
|
||
if (path.length === 1) { | ||
obj[key] = value; | ||
return; | ||
} | ||
|
||
if (!isArray(obj[key]) || !isObject(obj[key])) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition doesn't quite do what you want. Lifting the negation, you get There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, my bad. It should be |
||
var nextKey = path[1]; | ||
obj[key] = isNumber(nextKey) ? [] : {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
return set(obj[key], path.slice(1), value); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,14 @@ | ||||||
import isArray from './isArray.js'; | ||||||
import isObject from './isObject.js'; | ||||||
import _set from './_set.js'; | ||||||
|
||||||
|
||||||
// set the value in given path | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Underscore is literate code. Please elaborate a bit and write full sentences. |
||||||
export default function set (obj, path, value) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
if (!isObject(obj) || !isArray(path)) return obj; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By convention, Underscore functions allow you to pass a single path component as a bare string or number instead of an array. Take |
||||||
if (path.length === 0) return obj; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're checking There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't see other option better than this. I like this implementation. Please let me know if you have an idea how to improve it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please refer to the approach I suggested in #2948. If you do postorder assignment instead of preorder assignment, you can check for Referring again to #2948, I also realize that your code is still vulnerable to prototype pollution. You need to fix that as well. |
||||||
|
||||||
_set(obj, path, value); | ||||||
|
||||||
return obj; | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1255,4 +1255,29 @@ | |||||||||
assert.deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes'); | ||||||||||
|
||||||||||
}); | ||||||||||
|
||||||||||
|
||||||||||
QUnit.test('set', function(assert) { | ||||||||||
// returns first argument if first argument is primitive type | ||||||||||
assert.strictEqual(_.set(undefined, ['some', 'path'], 'some value'), undefined); | ||||||||||
assert.strictEqual(_.set(null, ['some', 'path'], 'some value'), null); | ||||||||||
assert.strictEqual(_.set(12, ['some', 'path'], 'some value'), 12); | ||||||||||
assert.strictEqual(_.set(BigInt(1), ['some', 'path'], 'some value'), BigInt(1)); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are trying to support old environments, so you cannot just sprinkle post-ES3 features in the tests (or the source code, for that matter). To fix, make this line conditional on feature detection:
Suggested change
You'll find similar code elsewhere in this module. |
||||||||||
assert.strictEqual(_.set('string', ['some', 'path'], 'some value'), 'string'); | ||||||||||
assert.strictEqual(_.set(true, ['some', 'path'], 'some value'), true); | ||||||||||
|
||||||||||
// returns the same object if path is not an array or its empty | ||||||||||
assert.deepEqual(_.set({random: {object: true}}, undefined, 'some value'), {random: {object: true}}); | ||||||||||
assert.deepEqual(_.set({random: {object: true}}, {}, 'some value'), {random: {object: true}}); | ||||||||||
assert.deepEqual(_.set({random: {object: true}}, [], 'some value'), {random: {object: true}}); | ||||||||||
|
||||||||||
assert.deepEqual(_.set({x: {l: 10}}, ['x', 'l'], 'changed'), {x: {l: 'changed'}}); | ||||||||||
assert.deepEqual(_.set({x: 10}, ['my', 'path'], 'my value'), {x: 10, my: {path: 'my value'}}); | ||||||||||
assert.deepEqual(_.set({x: 10}, ['my', 0, 'path'], 'my value'), {x: 10, my: [{path: 'my value'}]}); | ||||||||||
assert.deepEqual(_.set({x: 10}, [0], 'my value'), {x: 10, '0': 'my value'}); | ||||||||||
assert.deepEqual(_.set({x: 10}, [0, 1], 'my value'), {x: 10, '0': [undefined, 'my value']}); | ||||||||||
assert.deepEqual(_.set({x: 10}, [0, 0], 'my value'), {x: 10, '0': ['my value']}); | ||||||||||
assert.deepEqual(_.set({x: 10}, [0, '0'], 'my value'), {x: 10, '0': {'0': 'my value'}}); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree with this difference. Numeric keys are implicitly converted to strings. Array indices are actually strings, too. The second test should generate the same value as the first.
Suggested change
|
||||||||||
assert.deepEqual(_.set({x: 10}, [0, 'my'], 'my value'), {x: 10, '0': {my: 'my value'}}); | ||||||||||
}); | ||||||||||
}()); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three changes needed for this internal function as a whole:
set
, so it does not need to be in a separate module (compareisEqual
). Please move it into./set.js
. You can call itdeepSet
(cf.deepGet
).