├── .editorconfig
├── .eslintrc.json
├── .gitattributes
├── .github
├── FUNDING.yml
└── workflows
│ └── main.yml
├── .gitignore
├── .verb.md
├── LICENSE
├── README.md
├── benchmark
├── code
│ ├── deep-object.js
│ ├── deep-property.js
│ ├── deephas.js
│ ├── dot-prop.js
│ ├── dot2val.js
│ ├── es5-dot-prop.js
│ ├── lodash-set.js
│ ├── object-path-set.js
│ ├── object-set.js
│ └── set-value.js
├── fixtures
│ ├── deep.js
│ ├── medium.js
│ └── shallow.js
├── index.js
├── package.json
├── stats.json
└── stats.md
├── examples.js
├── index.js
├── package.json
└── test
├── .eslintrc.json
└── test.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org/
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended"
4 | ],
5 |
6 | "env": {
7 | "es2021": true,
8 | "node": true
9 | },
10 |
11 | "parserOptions": {
12 | "ecmaVersion": 12
13 | },
14 |
15 | "rules": {
16 | "accessor-pairs": 2,
17 | "arrow-parens": [2, "as-needed"],
18 | "arrow-spacing": [2, { "before": true, "after": true }],
19 | "block-spacing": [2, "always"],
20 | "brace-style": [2, "1tbs", { "allowSingleLine": true }],
21 | "comma-dangle": [2, "never"],
22 | "comma-spacing": [2, { "before": false, "after": true }],
23 | "comma-style": [2, "last"],
24 | "constructor-super": 2,
25 | "curly": [2, "multi-line"],
26 | "dot-location": [2, "property"],
27 | "eol-last": 2,
28 | "eqeqeq": [2, "allow-null"],
29 | "generator-star-spacing": [2, { "before": true, "after": true }],
30 | "handle-callback-err": [2, "^(err|error)$"],
31 | "indent": [2, 2, { "SwitchCase": 1 }],
32 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
33 | "keyword-spacing": [2, { "before": true, "after": true }],
34 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
35 | "new-parens": 2,
36 | "no-array-constructor": 2,
37 | "no-caller": 2,
38 | "no-class-assign": 2,
39 | "no-cond-assign": 2,
40 | "no-const-assign": 2,
41 | "no-control-regex": 2,
42 | "no-debugger": 2,
43 | "no-delete-var": 2,
44 | "no-dupe-args": 2,
45 | "no-dupe-class-members": 2,
46 | "no-dupe-keys": 2,
47 | "no-duplicate-case": 2,
48 | "no-empty-character-class": 2,
49 | "no-eval": 2,
50 | "no-ex-assign": 2,
51 | "no-extend-native": 2,
52 | "no-extra-bind": 2,
53 | "no-extra-boolean-cast": 2,
54 | "no-extra-parens": [2, "functions"],
55 | "no-fallthrough": 2,
56 | "no-floating-decimal": 2,
57 | "no-func-assign": 2,
58 | "no-implied-eval": 2,
59 | "no-implicit-coercion": 2,
60 | "no-inner-declarations": [2, "functions"],
61 | "no-invalid-regexp": 2,
62 | "no-irregular-whitespace": 2,
63 | "no-iterator": 2,
64 | "no-label-var": 2,
65 | "no-labels": 2,
66 | "no-lone-blocks": 2,
67 | "no-lonely-if": 2,
68 | "no-mixed-spaces-and-tabs": 2,
69 | "no-multi-spaces": 0,
70 | "no-multi-str": 2,
71 | "no-multiple-empty-lines": [2, { "max": 1 }],
72 | "no-native-reassign": 2,
73 | "no-negated-in-lhs": 2,
74 | "no-new": 2,
75 | "no-new-func": 2,
76 | "no-new-object": 2,
77 | "no-new-require": 2,
78 | "no-new-wrappers": 2,
79 | "no-obj-calls": 2,
80 | "no-octal": 2,
81 | "no-octal-escape": 2,
82 | "no-proto": 2,
83 | "no-redeclare": 2,
84 | "no-regex-spaces": 2,
85 | "no-return-assign": 2,
86 | "no-self-compare": 2,
87 | "no-sequences": 2,
88 | "no-shadow-restricted-names": 2,
89 | "no-spaced-func": 2,
90 | "no-sparse-arrays": 2,
91 | "no-this-before-super": 2,
92 | "no-throw-literal": 2,
93 | "no-trailing-spaces": 2,
94 | "no-undef": 2,
95 | "no-undef-init": 2,
96 | "no-unexpected-multiline": 2,
97 | "no-unneeded-ternary": [2, { "defaultAssignment": false }],
98 | "no-unreachable": 2,
99 | "no-unused-expressions": 2,
100 | "no-unused-vars": [2, { "vars": "all", "args": "none" }],
101 | "no-useless-call": 2,
102 | "no-with": 2,
103 | "object-curly-spacing": ["error", "always", { "objectsInObjects": true }],
104 | "one-var": [2, { "initialized": "never" }],
105 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
106 | "padded-blocks": [0, "never"],
107 | "prefer-const": [2, { "destructuring": "all", "ignoreReadBeforeAssign": false }],
108 | "quotes": [2, "single", "avoid-escape"],
109 | "radix": 2,
110 | "semi": [2, "always"],
111 | "semi-spacing": [2, { "before": false, "after": true }],
112 | "space-before-blocks": [2, "always"],
113 | "space-before-function-paren": [2, { "anonymous": "never", "named": "never", "asyncArrow": "always" }],
114 | "space-in-parens": [2, "never"],
115 | "space-infix-ops": 2,
116 | "space-unary-ops": [2, { "words": true, "nonwords": false }],
117 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
118 | "strict": 2,
119 | "use-isnan": 2,
120 | "valid-typeof": 2,
121 | "wrap-iife": [2, "any"],
122 | "yoda": [2, "never"]
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Enforce Unix newlines
2 | * text eol=lf
3 |
4 | # binaries
5 | *.ai binary
6 | *.psd binary
7 | *.jpg binary
8 | *.gif binary
9 | *.png binary
10 | *.jpeg binary
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [jonschlinkert, doowb]
4 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | test:
6 | name: Node.js ${{ matrix.node-version }} @ ${{ matrix.os }}
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | fail-fast: false
10 | matrix:
11 | os: [ubuntu-latest, windows-latest, macos-latest]
12 | node-version: [11, 12, 13, 14, 15, 16]
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: actions/setup-node@v1
17 | with:
18 | node-version: ${{ matrix.node-version }}
19 | - run: npm install
20 | - run: npm test
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # always ignore files
2 | *.DS_Store
3 | .idea
4 | .vscode
5 | *.sublime-*
6 |
7 | # test related, or directories generated by tests
8 | test/actual
9 | actual
10 | coverage
11 | .nyc*
12 |
13 | # npm
14 | node_modules
15 | npm-debug.log
16 |
17 | # yarn
18 | yarn.lock
19 | yarn-error.log
20 |
21 | # misc
22 | _gh_pages
23 | _draft
24 | _drafts
25 | bower_components
26 | vendor
27 | temp
28 | tmp
29 | TODO.md
30 | package-lock.json
--------------------------------------------------------------------------------
/.verb.md:
--------------------------------------------------------------------------------
1 | ## Heads up!
2 |
3 | [Please update][update] to version 3.0.1 or later, a critical bug was fixed in that version.
4 |
5 | ## Usage
6 |
7 | ```js
8 | const set = require('{%= name %}');
9 |
10 | const obj = {};
11 | set(obj, 'a.b.c', 'd');
12 |
13 | console.log(obj);
14 | //=> { a: { b: { c: 'd' } } }
15 | ```
16 |
17 | ### Params
18 |
19 | Signature:
20 |
21 | ```js
22 | set(object, property_path, value[, options]);
23 | ```
24 |
25 | - `object` **{Object}**: The object to set `value` on
26 | - `path` **{String|Symbol|Array}**: The [path](#object-paths) of the property to set.
27 | - `value` **{any}**: The value to set on `obj[prop]`
28 | - `options` **{Object}**: See all [available options](#options)
29 |
30 | ### Object paths
31 |
32 | You may pass a string, symbol, or array of strings or symbols. By default, when a string is passed this library will split the string on `.` or a [custom separator](#options-separator) It's useful to pass an array
33 |
34 |
35 | ### Escaping
36 |
37 | **Escaping with backslashes**
38 |
39 | Prevent set-value from splitting on a dot by prefixing it with backslashes:
40 |
41 | ```js
42 | console.log(set({}, 'a\\.b.c', 'd'));
43 | //=> { 'a.b': { c: 'd' } }
44 |
45 | console.log(set({}, 'a\\.b\\.c', 'd'));
46 | //=> { 'a.b.c': 'd' }
47 | ```
48 |
49 | ## Options
50 |
51 | ### options.preservePaths
52 |
53 | Do not split properties that include a `/`. By default, set-value assumes that properties with a `/` are not intended to be split. This option allows you to disable default behavior.
54 |
55 | Note that this option cannot be used if `options.separator` is set to `/`.
56 |
57 | **Type**: `boolean`
58 |
59 | **Default**: `true`
60 |
61 | **Example**
62 |
63 | ```js
64 | console.log(set({}, 'https://github.com', true));
65 | //=> { 'https://github.com': true }
66 |
67 | console.log(set({}, 'https://github.com', true, { preservePaths: false }));
68 | //=> { 'https://github': { com: true } }
69 | ```
70 |
71 | ### options.separator
72 |
73 | Custom separator to use for splitting object paths.
74 |
75 | **Type**: `string`
76 |
77 | **Default**: `.`
78 |
79 | **Example**
80 |
81 | ```js
82 | console.log(set(obj, 'auth/userpass/users/bob', '*****', { separator: '/' }));
83 | //=> { auth: { userpass: { users: { bob: '*****' } } } }
84 | ```
85 |
86 | ### options.split
87 |
88 | Custom `.split()` function to use.
89 |
90 |
91 | ### options.merge
92 |
93 | Allows you to update plain object values, instead of overwriting them.
94 |
95 | **Type**: `boolean|function` - A custom `merge` function may be defined if you need to deep merge. Otherwise, when `merge` is `true`, a shallow merge will be performed by `Object.assign()`.
96 |
97 | **Default**: `undefined`
98 |
99 | **Example**
100 |
101 | ```js
102 | const obj = { foo: { bar: { baz: 'qux' } } };
103 | set(obj, 'foo.bar.fez', 'zzz', { merge: true });
104 | //=> { foo: { bar: { baz: 'qux', fez: 'zzz' } } }
105 | ```
106 |
107 | ## Benchmarks
108 |
109 | Benchmarks were run on a MacBook Pro 2.5 GHz Intel Core i7, 16 GB 1600 MHz DDR3.
110 |
111 | ```
112 | {%= include("./benchmark/stats.md") %}
113 | ```
114 |
115 | ### Running the benchmarks
116 |
117 | Clone this library into a local directory:
118 |
119 | ```sh
120 | $ git clone https://github.com/jonschlinkert/set-value.git
121 | ```
122 |
123 | Then install devDependencies and run benchmarks:
124 |
125 | ```sh
126 | $ npm install && node benchmark
127 | ```
128 |
129 | ## Comparisons to other libs, or _"the list of shame"_
130 |
131 | These are just a few of the duplicate libraries on NPM.
132 |
133 | - [bury][] fails all of the tests. I even wrapped it to have it return the object instead of the value, but with all of that work it still fails the vast majority of tests.
134 | - [deep-get-set][] fails 22 of 26 unit tests.
135 | - [deep-object][] fails 25 of 26 unit tests, completely butchered given objects.
136 | - [deep-property][] fails 17 of 26 unit tests.
137 | - [deep-set][] fails 13 of 26 unit tests.
138 | - [deephas][] fails 17 of 26 unit tests.
139 | - [dot-prop][] fails 9 of 26 unit tests.
140 | - [dot2val][] fails 17 of 26 unit tests.
141 | - [es5-dot-prop][] fails 15 of 26 unit tests.
142 | - [getsetdeep][] fails all unit tests due to `this` being used improperly in the methods. I was able to patch it by binding the (plain) object to the methods, but it still fails 17 of 26 unit tests.
143 | - [lodash.set][] fails 11 of 26 unit tests.
144 | - [object-path-set][] fails 12 of 26 unit tests.
145 | - [object-path][] fails 16 of 26 unit tests.
146 | - [object-set][] fails 13 of 26 unit tests.
147 | - [set-nested-prop][] fails 24 of 26 unit tests.
148 | - [setvalue][] (this library is almost identical to a previous version of this library)
149 | - Many dozens of others
150 |
151 | **Others that do the same thing, but use a completely different API**
152 |
153 | - [deep-set-in][]
154 | - [set-deep][]
155 | - [set-deep-prop][]
156 | - [bury][]
157 | - Many dozens of others
158 |
159 |
160 | ## History
161 |
162 | ### v3.0.0
163 |
164 | - Added support for a custom `split` function to be passed on the options.
165 | - Removed support for splitting on brackets, since a [custom function][split-string] can be passed to do this now.
166 |
167 | ### v2.0.0
168 |
169 | - Adds support for escaping with double or single quotes. See [escaping](#escaping) for examples.
170 | - Will no longer split inside brackets or braces. See [bracket support](#bracket-support) for examples.
171 |
172 | If there are any regressions please create a [bug report](../../issues/new). Thanks!
173 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-present, Jon Schlinkert.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # set-value [](https://www.npmjs.com/package/set-value) [](https://npmjs.org/package/set-value) [](https://npmjs.org/package/set-value) [](https://github.com/jonschlinkert/set-value/actions/workflows/main.yml)
2 |
3 | > Set nested properties on an object using dot notation.
4 |
5 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
6 |
7 | ## Install
8 |
9 | Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >=11.0):
10 |
11 | ```sh
12 | $ npm install --save set-value
13 | ```
14 |
15 | ## Heads up!
16 |
17 | [Please update](https://github.com/update/update) to version 3.0.1 or later, a critical bug was fixed in that version.
18 |
19 | ## Usage
20 |
21 | ```js
22 | const set = require('set-value');
23 |
24 | const obj = {};
25 | set(obj, 'a.b.c', 'd');
26 |
27 | console.log(obj);
28 | //=> { a: { b: { c: 'd' } } }
29 | ```
30 |
31 | ### Params
32 |
33 | Signature:
34 |
35 | ```js
36 | set(object, property_path, value[, options]);
37 | ```
38 |
39 | * `object` **{Object}**: The object to set `value` on
40 | * `path` **{String|Symbol|Array}**: The [path](#object-paths) of the property to set.
41 | * `value` **{any}**: The value to set on `obj[prop]`
42 | * `options` **{Object}**: See all [available options](#options)
43 |
44 | ### Object paths
45 |
46 | You may pass a string, symbol, or array of strings or symbols. By default, when a string is passed this library will split the string on `.` or a [custom separator](#options-separator) It's useful to pass an array
47 |
48 | ### Escaping
49 |
50 | **Escaping with backslashes**
51 |
52 | Prevent set-value from splitting on a dot by prefixing it with backslashes:
53 |
54 | ```js
55 | console.log(set({}, 'a\\.b.c', 'd'));
56 | //=> { 'a.b': { c: 'd' } }
57 |
58 | console.log(set({}, 'a\\.b\\.c', 'd'));
59 | //=> { 'a.b.c': 'd' }
60 | ```
61 |
62 | ## Options
63 |
64 | ### options.preservePaths
65 |
66 | Do not split properties that include a `/`. By default, set-value assumes that properties with a `/` are not intended to be split. This option allows you to disable default behavior.
67 |
68 | Note that this option cannot be used if `options.separator` is set to `/`.
69 |
70 | **Type**: `boolean`
71 |
72 | **Default**: `true`
73 |
74 | **Example**
75 |
76 | ```js
77 | console.log(set({}, 'https://github.com', true));
78 | //=> { 'https://github.com': true }
79 |
80 | console.log(set({}, 'https://github.com', true, { preservePaths: false }));
81 | //=> { 'https://github': { com: true } }
82 | ```
83 |
84 | ### options.separator
85 |
86 | Custom separator to use for splitting object paths.
87 |
88 | **Type**: `string`
89 |
90 | **Default**: `.`
91 |
92 | **Example**
93 |
94 | ```js
95 | console.log(set(obj, 'auth/userpass/users/bob', '*****', { separator: '/' }));
96 | //=> { auth: { userpass: { users: { bob: '*****' } } } }
97 | ```
98 |
99 | ### options.split
100 |
101 | Custom `.split()` function to use.
102 |
103 | ### options.merge
104 |
105 | Allows you to update plain object values, instead of overwriting them.
106 |
107 | **Type**: `boolean|function` - A custom `merge` function may be defined if you need to deep merge. Otherwise, when `merge` is `true`, a shallow merge will be performed by `Object.assign()`.
108 |
109 | **Default**: `undefined`
110 |
111 | **Example**
112 |
113 | ```js
114 | const obj = { foo: { bar: { baz: 'qux' } } };
115 | set(obj, 'foo.bar.fez', 'zzz', { merge: true });
116 | //=> { foo: { bar: { baz: 'qux', fez: 'zzz' } } }
117 | ```
118 |
119 | ## Benchmarks
120 |
121 | Benchmarks were run on a MacBook Pro 2.5 GHz Intel Core i7, 16 GB 1600 MHz DDR3.
122 |
123 | ```
124 | # deep (194 bytes)
125 | deep-object x 823,287 ops/sec ±1.00% (90 runs sampled)
126 | deep-property x 1,787,990 ops/sec ±0.82% (92 runs sampled)
127 | deephas x 840,700 ops/sec ±0.95% (93 runs sampled)
128 | dot-prop x 1,249,663 ops/sec ±0.89% (90 runs sampled)
129 | dot2val x 2,067,212 ops/sec ±1.08% (91 runs sampled)
130 | es5-dot-prop x 1,668,806 ops/sec ±0.92% (92 runs sampled)
131 | lodash-set x 1,286,725 ops/sec ±0.82% (90 runs sampled)
132 | object-path-set x 1,261,242 ops/sec ±1.63% (90 runs sampled)
133 | object-set x 285,369 ops/sec ±0.91% (90 runs sampled)
134 | set-value x 2,076,931 ops/sec ±0.86% (93 runs sampled)
135 |
136 | fastest is set-value, dot2val (by 203% avg)
137 |
138 | # medium (98 bytes)
139 | deep-object x 5,811,161 ops/sec ±1.12% (90 runs sampled)
140 | deep-property x 4,075,885 ops/sec ±0.91% (90 runs sampled)
141 | deephas x 1,508,136 ops/sec ±0.82% (92 runs sampled)
142 | dot-prop x 2,809,838 ops/sec ±1.16% (87 runs sampled)
143 | dot2val x 4,600,890 ops/sec ±0.76% (91 runs sampled)
144 | es5-dot-prop x 3,263,790 ops/sec ±0.97% (91 runs sampled)
145 | lodash-set x 3,486,628 ops/sec ±1.20% (90 runs sampled)
146 | object-path-set x 3,729,018 ops/sec ±0.90% (92 runs sampled)
147 | object-set x 973,961 ops/sec ±0.80% (92 runs sampled)
148 | set-value x 6,941,474 ops/sec ±1.24% (90 runs sampled)
149 |
150 | fastest is set-value (by 206% avg)
151 |
152 | # shallow (101 bytes)
153 | deep-object x 9,416,410 ops/sec ±1.19% (89 runs sampled)
154 | deep-property x 5,108,536 ops/sec ±0.98% (93 runs sampled)
155 | deephas x 1,706,979 ops/sec ±0.98% (86 runs sampled)
156 | dot-prop x 4,045,902 ops/sec ±1.10% (92 runs sampled)
157 | dot2val x 5,862,418 ops/sec ±0.88% (91 runs sampled)
158 | es5-dot-prop x 4,439,646 ops/sec ±1.18% (90 runs sampled)
159 | lodash-set x 9,303,292 ops/sec ±1.19% (89 runs sampled)
160 | object-path-set x 5,657,479 ops/sec ±0.95% (93 runs sampled)
161 | object-set x 2,020,041 ops/sec ±0.92% (91 runs sampled)
162 | set-value x 11,272,227 ops/sec ±1.36% (88 runs sampled)
163 |
164 | fastest is set-value (by 213% avg)
165 |
166 | ```
167 |
168 | ### Running the benchmarks
169 |
170 | Clone this library into a local directory:
171 |
172 | ```sh
173 | $ git clone https://github.com/jonschlinkert/set-value.git
174 | ```
175 |
176 | Then install devDependencies and run benchmarks:
177 |
178 | ```sh
179 | $ npm install && node benchmark
180 | ```
181 |
182 | ## Comparisons to other libs, or _"the list of shame"_
183 |
184 | These are just a few of the duplicate libraries on NPM.
185 |
186 | * [bury](https://github.com/kalmbach/bury) fails all of the tests. I even wrapped it to have it return the object instead of the value, but with all of that work it still fails the vast majority of tests.
187 | * [deep-get-set](https://github.com/acstll/deep-get-set) fails 22 of 26 unit tests.
188 | * [deep-object](https://github.com/ayushgp/deep-object) fails 25 of 26 unit tests, completely butchered given objects.
189 | * [deep-property](https://github.com/mikattack/node-deep-property) fails 17 of 26 unit tests.
190 | * [deep-set](https://github.com/klaemo/deep-set) fails 13 of 26 unit tests.
191 | * [deephas](https://github.com/sharpred/deepHas) fails 17 of 26 unit tests.
192 | * [dot-prop](https://github.com/sindresorhus/dot-prop) fails 9 of 26 unit tests.
193 | * [dot2val](https://github.com/yangg/dot2val) fails 17 of 26 unit tests.
194 | * [es5-dot-prop](https://github.com/sindresorhus/dot-prop) fails 15 of 26 unit tests.
195 | * [getsetdeep](https://github.com/bevry/getsetdeep) fails all unit tests due to `this` being used improperly in the methods. I was able to patch it by binding the (plain) object to the methods, but it still fails 17 of 26 unit tests.
196 | * [lodash.set](https://lodash.com/) fails 11 of 26 unit tests.
197 | * [object-path-set](https://github.com/skratchdot/object-path-set) fails 12 of 26 unit tests.
198 | * [object-path](https://github.com/mariocasciaro/object-path) fails 16 of 26 unit tests.
199 | * [object-set](https://github.com/gearcase/object-set) fails 13 of 26 unit tests.
200 | * [set-nested-prop](https://github.com/tiaanduplessis/set-nested-prop) fails 24 of 26 unit tests.
201 | * [setvalue](https://github.com/blakeembrey/setvalue) (this library is almost identical to a previous version of this library)
202 | * Many dozens of others
203 |
204 | **Others that do the same thing, but use a completely different API**
205 |
206 | * [deep-set-in](https://github.com/KulikovskyIgor/deep-set-in)
207 | * [set-deep](https://github.com/radubrehar/set-deep)
208 | * [set-deep-prop](https://github.com/mmckelvy/set-deep-prop)
209 | * [bury](https://github.com/kalmbach/bury)
210 | * Many dozens of others
211 |
212 | ## History
213 |
214 | ### v3.0.0
215 |
216 | * Added support for a custom `split` function to be passed on the options.
217 | * Removed support for splitting on brackets, since a [custom function](https://github.com/jonschlinkert/split-string) can be passed to do this now.
218 |
219 | ### v2.0.0
220 |
221 | * Adds support for escaping with double or single quotes. See [escaping](#escaping) for examples.
222 | * Will no longer split inside brackets or braces. See [bracket support](#bracket-support) for examples.
223 |
224 | If there are any regressions please create a [bug report](../../issues/new). Thanks!
225 |
226 | ## About
227 |
228 |
229 | Contributing
230 |
231 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
232 |
233 |
234 |
235 |
236 | Running Tests
237 |
238 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
239 |
240 | ```sh
241 | $ npm install && npm test
242 | ```
243 |
244 |
245 |
246 |
247 | Building docs
248 |
249 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
250 |
251 | To generate the readme, run the following command:
252 |
253 | ```sh
254 | $ npm install -g verbose/verb#dev verb-generate-readme && verb
255 | ```
256 |
257 |
258 |
259 | ### Related projects
260 |
261 | You might also be interested in these projects:
262 |
263 | * [assign-value](https://www.npmjs.com/package/assign-value): Assign a value or extend a deeply nested property of an object using object path… [more](https://github.com/jonschlinkert/assign-value) | [homepage](https://github.com/jonschlinkert/assign-value "Assign a value or extend a deeply nested property of an object using object path notation.")
264 | * [get-value](https://www.npmjs.com/package/get-value): Use property paths like 'a.b.c' to get a nested value from an object. Even works… [more](https://github.com/jonschlinkert/get-value) | [homepage](https://github.com/jonschlinkert/get-value "Use property paths like 'a.b.c' to get a nested value from an object. Even works when keys have dots in them (no other dot-prop library can do this!).")
265 | * [has-value](https://www.npmjs.com/package/has-value): Returns true if a value exists, false if empty. Works with deeply nested values using… [more](https://github.com/jonschlinkert/has-value) | [homepage](https://github.com/jonschlinkert/has-value "Returns true if a value exists, false if empty. Works with deeply nested values using object paths.")
266 | * [merge-value](https://www.npmjs.com/package/merge-value): Similar to assign-value but deeply merges object values or nested values using object path/dot notation. | [homepage](https://github.com/jonschlinkert/merge-value "Similar to assign-value but deeply merges object values or nested values using object path/dot notation.")
267 | * [omit-value](https://www.npmjs.com/package/omit-value): Omit properties from an object or deeply nested property of an object using object path… [more](https://github.com/jonschlinkert/omit-value) | [homepage](https://github.com/jonschlinkert/omit-value "Omit properties from an object or deeply nested property of an object using object path notation.")
268 | * [set-value](https://www.npmjs.com/package/set-value): Set nested properties on an object using dot notation. | [homepage](https://github.com/jonschlinkert/set-value "Set nested properties on an object using dot notation.")
269 | * [union-value](https://www.npmjs.com/package/union-value): Set an array of unique values as the property of an object. Supports setting deeply… [more](https://github.com/jonschlinkert/union-value) | [homepage](https://github.com/jonschlinkert/union-value "Set an array of unique values as the property of an object. Supports setting deeply nested properties using using object-paths/dot notation.")
270 | * [unset-value](https://www.npmjs.com/package/unset-value): Delete nested properties from an object using dot notation. | [homepage](https://github.com/jonschlinkert/unset-value "Delete nested properties from an object using dot notation.")
271 |
272 | ### Contributors
273 |
274 | | **Commits** | **Contributor** |
275 | | --- | --- |
276 | | 87 | [jonschlinkert](https://github.com/jonschlinkert) |
277 | | 4 | [doowb](https://github.com/doowb) |
278 | | 2 | [mbelsky](https://github.com/mbelsky) |
279 | | 1 | [dkebler](https://github.com/dkebler) |
280 | | 1 | [GlennKintscher](https://github.com/GlennKintscher) |
281 | | 1 | [petermorlion](https://github.com/petermorlion) |
282 | | 1 | [abetomo](https://github.com/abetomo) |
283 | | 1 | [zeidoo](https://github.com/zeidoo) |
284 | | 1 | [ready-research](https://github.com/ready-research) |
285 | | 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
286 |
287 | ### Author
288 |
289 | **Jon Schlinkert**
290 |
291 | * [GitHub Profile](https://github.com/jonschlinkert)
292 | * [Twitter Profile](https://twitter.com/jonschlinkert)
293 | * [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
294 |
295 | ### License
296 |
297 | Copyright © 2021, [Jon Schlinkert](https://github.com/jonschlinkert).
298 | Released under the [MIT License](LICENSE).
299 |
300 | ***
301 |
302 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on September 12, 2021._
--------------------------------------------------------------------------------
/benchmark/code/deep-object.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { set } = require('deep-object');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/benchmark/code/deep-property.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { set } = require('deep-property');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/benchmark/code/deephas.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { set } = require('deephas');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
--------------------------------------------------------------------------------
/benchmark/code/dot-prop.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('dot-prop').set;
4 |
--------------------------------------------------------------------------------
/benchmark/code/dot2val.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { set } = require('dot2val');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/benchmark/code/es5-dot-prop.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { set } = require('es5-dot-prop');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/benchmark/code/lodash-set.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('lodash.set');
4 |
--------------------------------------------------------------------------------
/benchmark/code/object-path-set.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const set = require('object-path-set');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/benchmark/code/object-set.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const set = require('object-set');
4 |
5 | module.exports = (obj, path, value) => {
6 | try {
7 | set(obj, path, value);
8 | } catch (err) {
9 | // do nothing
10 | }
11 | return obj;
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/benchmark/code/set-value.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const set = require('../..');
4 |
5 | module.exports = (obj, key, value) => set(obj, key, value);
6 |
--------------------------------------------------------------------------------
/benchmark/fixtures/deep.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Deeply nested values
3 | */
4 |
5 | module.exports = [{}, 'a.b.c.d.e.f.g.h.i.j.k.l.m.n.o', 'LAST VALUE!', {a: {b: {c: {d: {e: {f: {g: {h: {i: {j: {k: {l: {m: {n: {o: 'LAST VALUE!'}}}}}}}}}}}}}}}];
6 |
--------------------------------------------------------------------------------
/benchmark/fixtures/medium.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Deeply nested values
3 | */
4 |
5 | module.exports = [{}, 'x.y.z', 'foooo', {x: {y: {z: 'foooo'}}}];
6 |
--------------------------------------------------------------------------------
/benchmark/fixtures/shallow.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Deeply nested values
3 | */
4 |
5 | module.exports = [{}, 'foo', {i: {k: 'L'}}, { foo: {i: {k: 'L'}}}];
6 |
--------------------------------------------------------------------------------
/benchmark/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const argv = require('minimist')(process.argv.slice(2));
5 | const bench = require('benchmarked');
6 | const write = require('write');
7 |
8 | bench.run({ fixtures: 'fixtures/*.js', code: 'code/*.js', dry: Boolean(argv['dry-run']) })
9 | .then(function(stats) {
10 | write.sync(path.join(__dirname, 'stats.json'), JSON.stringify(stats, null, 2));
11 | write.sync(path.join(__dirname, 'stats.md'), bench.render(stats));
12 | })
13 | .catch(console.error);
14 |
--------------------------------------------------------------------------------
/benchmark/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "set-value-benchmarks",
3 | "description": "Node.js library and API for set-value-benchmarks",
4 | "version": "0.0.0",
5 | "private": true,
6 | "dependencies": {
7 | "benchmarked": "^2.0.0",
8 | "minimist": "^1.2.5",
9 | "write": "^2.0.0"
10 | },
11 | "devDependencies": {
12 | "deep-object": "^1.0.0",
13 | "deep-property": "^1.1.0",
14 | "deep-set": "^1.0.1",
15 | "deephas": "^1.0.7",
16 | "dot-prop": "^6.0.1",
17 | "dot2val": "^1.2.2",
18 | "es5-dot-prop": "^4.1.1",
19 | "lodash.set": "^4.3.2",
20 | "object-path-set": "^1.0.1",
21 | "object-set": "^1.0.1"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/benchmark/stats.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "deep",
4 | "file": {
5 | "stat": null,
6 | "_contents": null,
7 | "history": [
8 | "/Users/jonschlinkert/dev/js-utils/objects/set-value/benchmark/fixtures/deep.js"
9 | ],
10 | "_cwd": "/Users/jonschlinkert/dev/js-utils/objects/set-value",
11 | "_isVinyl": true,
12 | "_symlink": null,
13 | "key": "deep",
14 | "content": [
15 | {
16 | "a": {
17 | "b": {
18 | "c": {
19 | "d": {
20 | "e": {
21 | "f": {
22 | "g": {
23 | "h": {
24 | "i": {
25 | "j": {
26 | "k": {
27 | "l": {
28 | "m": {
29 | "n": {
30 | "o": "LAST VALUE!"
31 | }
32 | }
33 | }
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | }
42 | }
43 | }
44 | }
45 | },
46 | "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o",
47 | "LAST VALUE!",
48 | {
49 | "a": {
50 | "b": {
51 | "c": {
52 | "d": {
53 | "e": {
54 | "f": {
55 | "g": {
56 | "h": {
57 | "i": {
58 | "j": {
59 | "k": {
60 | "l": {
61 | "m": {
62 | "n": {
63 | "o": "LAST VALUE!"
64 | }
65 | }
66 | }
67 | }
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
74 | }
75 | }
76 | }
77 | }
78 | }
79 | ],
80 | "bytes": "(194 bytes)",
81 | "suite": {
82 | "0": {
83 | "name": "set-value",
84 | "options": {
85 | "async": false,
86 | "defer": false,
87 | "delay": 0.005,
88 | "initCount": 1,
89 | "maxTime": 5,
90 | "minSamples": 5,
91 | "minTime": 0.05
92 | },
93 | "async": false,
94 | "defer": false,
95 | "delay": 0.005,
96 | "initCount": 1,
97 | "maxTime": 5,
98 | "minSamples": 5,
99 | "minTime": 0.05,
100 | "events": {
101 | "abort": [
102 | null
103 | ],
104 | "complete": [
105 | null
106 | ],
107 | "cycle": [
108 | null
109 | ],
110 | "error": [
111 | null
112 | ]
113 | },
114 | "id": 1,
115 | "stats": {
116 | "moe": 3.810901178799246e-9,
117 | "rme": 0.8959004252713377,
118 | "sem": 1.9443373361220644e-9,
119 | "deviation": 1.8342841743328754e-8,
120 | "mean": 4.253710648306762e-7,
121 | "sample": [
122 | 4.124695904213662e-7,
123 | 4.7009577019029703e-7,
124 | 4.1375394601926384e-7,
125 | 4.187159673499706e-7,
126 | 4.29260489750442e-7,
127 | 4.542271292356631e-7,
128 | 4.458841263049112e-7,
129 | 4.181324893629046e-7,
130 | 4.08162167273415e-7,
131 | 4.405099364884245e-7,
132 | 4.0496943406931114e-7,
133 | 4.196227443934881e-7,
134 | 4.533282902305643e-7,
135 | 4.7281762879611684e-7,
136 | 4.2752410445526614e-7,
137 | 4.5114443761524276e-7,
138 | 3.964522638803524e-7,
139 | 4.3548496524986995e-7,
140 | 4.2174224228956864e-7,
141 | 4.0180679400501156e-7,
142 | 4.411657841236821e-7,
143 | 4.0904827982916487e-7,
144 | 4.2758062660551904e-7,
145 | 4.062287519896617e-7,
146 | 4.2483961357225033e-7,
147 | 3.958674530755047e-7,
148 | 4.2714552503427734e-7,
149 | 4.024448804627047e-7,
150 | 4.136043449482294e-7,
151 | 4.388891699368036e-7,
152 | 4.194249286873749e-7,
153 | 4.4161789828692103e-7,
154 | 4.1910239074590645e-7,
155 | 4.277524624525239e-7,
156 | 4.0223467763541515e-7,
157 | 4.408363670748428e-7,
158 | 4.042942650465699e-7,
159 | 4.049482766772257e-7,
160 | 4.4092800182812476e-7,
161 | 4.332984886451389e-7,
162 | 4.2993797771578966e-7,
163 | 4.1214870061305216e-7,
164 | 4.228288890990182e-7,
165 | 4.257713425685153e-7,
166 | 4.7044721289773537e-7,
167 | 4.4872404772035994e-7,
168 | 4.5691524435408883e-7,
169 | 4.4584328558145395e-7,
170 | 4.3605419759507037e-7,
171 | 4.3901979417836827e-7,
172 | 4.2798011914330295e-7,
173 | 3.9513171954044726e-7,
174 | 4.1999102485304085e-7,
175 | 4.1657109198934646e-7,
176 | 4.2225151687075475e-7,
177 | 4.301233669014862e-7,
178 | 4.107231336579831e-7,
179 | 4.258073613540731e-7,
180 | 3.9610634642964084e-7,
181 | 4.15103273288891e-7,
182 | 4.212582451675738e-7,
183 | 4.1837121515587076e-7,
184 | 4.18461148157748e-7,
185 | 4.4809447285122963e-7,
186 | 4.263997659385628e-7,
187 | 4.5855999591570644e-7,
188 | 4.0928287659935435e-7,
189 | 4.20754101965959e-7,
190 | 3.9890959573348413e-7,
191 | 4.285326390034324e-7,
192 | 4.07307460904671e-7,
193 | 3.98448628227181e-7,
194 | 4.4001270058201183e-7,
195 | 4.2113036750787404e-7,
196 | 4.3667451322290034e-7,
197 | 4.03753175145503e-7,
198 | 4.4546282900162584e-7,
199 | 4.405591162217143e-7,
200 | 4.5685275336365136e-7,
201 | 4.10804668661687e-7,
202 | 4.1720792981298653e-7,
203 | 4.365575374851748e-7,
204 | 4.1525157677293685e-7,
205 | 4.214629075457323e-7,
206 | 4.058729234901859e-7,
207 | 4.2954227126868594e-7,
208 | 3.961749863035141e-7,
209 | 4.3700438287547935e-7,
210 | 4.2448363465602257e-7
211 | ],
212 | "variance": 3.364598432208039e-16
213 | },
214 | "times": {
215 | "cycle": 0.0543496609534155,
216 | "elapsed": 5.489,
217 | "period": 4.253710648306762e-7,
218 | "timeStamp": 1631437113733
219 | },
220 | "running": false,
221 | "count": 127770,
222 | "cycles": 7,
223 | "hz": 2350888.6303727813
224 | },
225 | "name": "(194 bytes)",
226 | "options": {
227 | "name": "deep"
228 | },
229 | "events": {
230 | "start": [
231 | null
232 | ],
233 | "complete": [
234 | null
235 | ]
236 | },
237 | "length": 1,
238 | "running": false
239 | }
240 | },
241 | "results": [
242 | {
243 | "name": "set-value",
244 | "file": {
245 | "stat": null,
246 | "_contents": null,
247 | "history": [
248 | "/Users/jonschlinkert/dev/js-utils/objects/set-value/benchmark/code/set-value.js"
249 | ],
250 | "_cwd": "/Users/jonschlinkert/dev/js-utils/objects/set-value",
251 | "_isVinyl": true,
252 | "_symlink": null,
253 | "key": "set-value"
254 | },
255 | "runs": 89,
256 | "hz": 2350888.6303727813,
257 | "ops": "2,350,889",
258 | "rme": "0.90"
259 | }
260 | ]
261 | },
262 | {
263 | "name": "medium",
264 | "file": {
265 | "stat": null,
266 | "_contents": null,
267 | "history": [
268 | "/Users/jonschlinkert/dev/js-utils/objects/set-value/benchmark/fixtures/medium.js"
269 | ],
270 | "_cwd": "/Users/jonschlinkert/dev/js-utils/objects/set-value",
271 | "_isVinyl": true,
272 | "_symlink": null,
273 | "key": "medium",
274 | "content": [
275 | {
276 | "x": {
277 | "y": {
278 | "z": "foooo"
279 | }
280 | }
281 | },
282 | "x.y.z",
283 | "foooo",
284 | {
285 | "x": {
286 | "y": {
287 | "z": "foooo"
288 | }
289 | }
290 | }
291 | ],
292 | "bytes": "(98 bytes)",
293 | "suite": {
294 | "0": {
295 | "name": "set-value",
296 | "options": {
297 | "async": false,
298 | "defer": false,
299 | "delay": 0.005,
300 | "initCount": 1,
301 | "maxTime": 5,
302 | "minSamples": 5,
303 | "minTime": 0.05
304 | },
305 | "async": false,
306 | "defer": false,
307 | "delay": 0.005,
308 | "initCount": 1,
309 | "maxTime": 5,
310 | "minSamples": 5,
311 | "minTime": 0.05,
312 | "events": {
313 | "abort": [
314 | null
315 | ],
316 | "complete": [
317 | null
318 | ],
319 | "cycle": [
320 | null
321 | ],
322 | "error": [
323 | null
324 | ]
325 | },
326 | "id": 2,
327 | "stats": {
328 | "moe": 1.0803601908881484e-9,
329 | "rme": 0.8070290968306569,
330 | "sem": 5.512041790245655e-10,
331 | "deviation": 5.229181984562506e-9,
332 | "mean": 1.3386880288838532e-7,
333 | "sample": [
334 | 1.2586806847317884e-7,
335 | 1.4585648785883063e-7,
336 | 1.2683933688602888e-7,
337 | 1.311958172037605e-7,
338 | 1.367016917198733e-7,
339 | 1.3342903574480922e-7,
340 | 1.3838017445075662e-7,
341 | 1.354059499271027e-7,
342 | 1.3037146196772408e-7,
343 | 1.4162180193521666e-7,
344 | 1.2669746614625822e-7,
345 | 1.317335826608602e-7,
346 | 1.3906323966817686e-7,
347 | 1.4370313374581143e-7,
348 | 1.3545984613646918e-7,
349 | 1.4097549980547432e-7,
350 | 1.266423543940107e-7,
351 | 1.440246010820393e-7,
352 | 1.2816768372384844e-7,
353 | 1.322993210489756e-7,
354 | 1.3904989182096807e-7,
355 | 1.39271037647299e-7,
356 | 1.3553541774036535e-7,
357 | 1.3842928776120443e-7,
358 | 1.2584332339928644e-7,
359 | 1.3275226628225142e-7,
360 | 1.3494471790045537e-7,
361 | 1.2825857900803937e-7,
362 | 1.3843763086675181e-7,
363 | 1.290115922111097e-7,
364 | 1.4372392810638144e-7,
365 | 1.3741903751783274e-7,
366 | 1.3534694472039018e-7,
367 | 1.3527674431183955e-7,
368 | 1.316260664532531e-7,
369 | 1.2756648521594603e-7,
370 | 1.3931558410904747e-7,
371 | 1.3402746593496294e-7,
372 | 1.3862562880788352e-7,
373 | 1.3321506581891316e-7,
374 | 1.3317759796617837e-7,
375 | 1.2511520891419692e-7,
376 | 1.3666665191457556e-7,
377 | 1.3134119128053068e-7,
378 | 1.3099377953491574e-7,
379 | 1.4267014570148652e-7,
380 | 1.3032739807534386e-7,
381 | 1.396849494249143e-7,
382 | 1.2737865913326547e-7,
383 | 1.3346059470596624e-7,
384 | 1.247158894773334e-7,
385 | 1.4161341407644534e-7,
386 | 1.3268476748245732e-7,
387 | 1.332347475671343e-7,
388 | 1.3507633715412494e-7,
389 | 1.336740230427663e-7,
390 | 1.379879131200181e-7,
391 | 1.301945382841351e-7,
392 | 1.2790126917157174e-7,
393 | 1.2603768913410143e-7,
394 | 1.379709506739247e-7,
395 | 1.2613686252526296e-7,
396 | 1.3256865869070275e-7,
397 | 1.372925585535083e-7,
398 | 1.3456888734811493e-7,
399 | 1.3554240734457444e-7,
400 | 1.325053156701629e-7,
401 | 1.271406562713598e-7,
402 | 1.335377112622381e-7,
403 | 1.4482470581871647e-7,
404 | 1.3094733011737748e-7,
405 | 1.36001804672479e-7,
406 | 1.3342936698777052e-7,
407 | 1.4096517031289185e-7,
408 | 1.3301769513328515e-7,
409 | 1.3401149187897385e-7,
410 | 1.2631596028737074e-7,
411 | 1.3643789861379517e-7,
412 | 1.3072781654299497e-7,
413 | 1.2626249625051017e-7,
414 | 1.385911777577805e-7,
415 | 1.299946228627908e-7,
416 | 1.3835222584468014e-7,
417 | 1.2814054563067649e-7,
418 | 1.3484052743643078e-7,
419 | 1.2839612315045659e-7,
420 | 1.3608970992471516e-7,
421 | 1.3435631217391731e-7,
422 | 1.2541887087494652e-7,
423 | 1.377563839674274e-7
424 | ],
425 | "variance": 2.7344344227673075e-17
426 | },
427 | "times": {
428 | "cycle": 0.05444738724836985,
429 | "elapsed": 5.489,
430 | "period": 1.3386880288838532e-7,
431 | "timeStamp": 1631437119222
432 | },
433 | "running": false,
434 | "count": 406722,
435 | "cycles": 8,
436 | "hz": 7470000.3169055125
437 | },
438 | "name": "(98 bytes)",
439 | "options": {
440 | "name": "medium"
441 | },
442 | "events": {
443 | "start": [
444 | null
445 | ],
446 | "complete": [
447 | null
448 | ]
449 | },
450 | "length": 1,
451 | "running": false
452 | }
453 | },
454 | "results": [
455 | {
456 | "name": "set-value",
457 | "file": {
458 | "stat": null,
459 | "_contents": null,
460 | "history": [
461 | "/Users/jonschlinkert/dev/js-utils/objects/set-value/benchmark/code/set-value.js"
462 | ],
463 | "_cwd": "/Users/jonschlinkert/dev/js-utils/objects/set-value",
464 | "_isVinyl": true,
465 | "_symlink": null,
466 | "key": "set-value"
467 | },
468 | "runs": 90,
469 | "hz": 7470000.3169055125,
470 | "ops": "7,470,000",
471 | "rme": "0.81"
472 | }
473 | ]
474 | },
475 | {
476 | "name": "shallow",
477 | "file": {
478 | "stat": null,
479 | "_contents": null,
480 | "history": [
481 | "/Users/jonschlinkert/dev/js-utils/objects/set-value/benchmark/fixtures/shallow.js"
482 | ],
483 | "_cwd": "/Users/jonschlinkert/dev/js-utils/objects/set-value",
484 | "_isVinyl": true,
485 | "_symlink": null,
486 | "key": "shallow",
487 | "content": [
488 | {
489 | "foo": {
490 | "i": {
491 | "k": "L"
492 | }
493 | }
494 | },
495 | "foo",
496 | {
497 | "i": {
498 | "k": "L"
499 | }
500 | },
501 | {
502 | "foo": {
503 | "i": {
504 | "k": "L"
505 | }
506 | }
507 | }
508 | ],
509 | "bytes": "(101 bytes)",
510 | "suite": {
511 | "0": {
512 | "name": "set-value",
513 | "options": {
514 | "async": false,
515 | "defer": false,
516 | "delay": 0.005,
517 | "initCount": 1,
518 | "maxTime": 5,
519 | "minSamples": 5,
520 | "minTime": 0.05
521 | },
522 | "async": false,
523 | "defer": false,
524 | "delay": 0.005,
525 | "initCount": 1,
526 | "maxTime": 5,
527 | "minSamples": 5,
528 | "minTime": 0.05,
529 | "events": {
530 | "abort": [
531 | null
532 | ],
533 | "complete": [
534 | null
535 | ],
536 | "cycle": [
537 | null
538 | ],
539 | "error": [
540 | null
541 | ]
542 | },
543 | "id": 3,
544 | "stats": {
545 | "moe": 7.325476289493897e-10,
546 | "rme": 0.903432224810049,
547 | "sem": 3.737487902803009e-10,
548 | "deviation": 3.525939035633339e-9,
549 | "mean": 8.108495677176131e-8,
550 | "sample": [
551 | 8.305923336477406e-8,
552 | 8.177847376646847e-8,
553 | 8.196885877491305e-8,
554 | 7.948966441426558e-8,
555 | 8.587929548049602e-8,
556 | 8.47644168781014e-8,
557 | 8.61609434574075e-8,
558 | 8.30022129571716e-8,
559 | 8.421524543078131e-8,
560 | 7.788224762683395e-8,
561 | 8.456992253113123e-8,
562 | 8.276447681235814e-8,
563 | 7.85972832877167e-8,
564 | 8.706829739070835e-8,
565 | 8.046921607528972e-8,
566 | 9.681608942805815e-8,
567 | 8.01013933946302e-8,
568 | 8.049219087370316e-8,
569 | 8.576562650552657e-8,
570 | 7.906724417710116e-8,
571 | 7.724164437531448e-8,
572 | 8.245908408925105e-8,
573 | 8.331908063275322e-8,
574 | 8.283924990938165e-8,
575 | 7.866853715199873e-8,
576 | 7.905276359237184e-8,
577 | 8.850674136783433e-8,
578 | 8.058689217886622e-8,
579 | 7.807339208604319e-8,
580 | 8.364810256116132e-8,
581 | 8.019336473720514e-8,
582 | 8.486670460085337e-8,
583 | 7.931103869739839e-8,
584 | 7.580877849403291e-8,
585 | 7.55876226886685e-8,
586 | 8.759498363144874e-8,
587 | 8.102292804629379e-8,
588 | 7.98568427185228e-8,
589 | 8.239872704139002e-8,
590 | 8.117721805566666e-8,
591 | 8.169286892631662e-8,
592 | 8.207891769841115e-8,
593 | 7.885031793778895e-8,
594 | 8.439302843916438e-8,
595 | 7.76215726495212e-8,
596 | 7.808337910909582e-8,
597 | 8.302041473593279e-8,
598 | 8.11676611921363e-8,
599 | 8.114628027408268e-8,
600 | 7.945343288119422e-8,
601 | 7.706396736328519e-8,
602 | 8.398616974379283e-8,
603 | 7.574974633322623e-8,
604 | 7.950717514588641e-8,
605 | 8.492352787345801e-8,
606 | 8.233461299939478e-8,
607 | 8.261256603181482e-8,
608 | 8.108616152483991e-8,
609 | 7.643010527731493e-8,
610 | 7.853419607432922e-8,
611 | 8.457282365862953e-8,
612 | 7.709262162182357e-8,
613 | 8.162701644537759e-8,
614 | 8.117974102826572e-8,
615 | 8.459285847710275e-8,
616 | 7.923084498307642e-8,
617 | 8.105100383302077e-8,
618 | 7.917051377184798e-8,
619 | 7.920606416821564e-8,
620 | 7.694740175298102e-8,
621 | 7.954250574898428e-8,
622 | 8.187964235072845e-8,
623 | 7.871995503669798e-8,
624 | 8.00944978730595e-8,
625 | 8.161824834142721e-8,
626 | 7.450728302635349e-8,
627 | 8.274469954669351e-8,
628 | 8.087301540654319e-8,
629 | 7.54707525475531e-8,
630 | 8.331316205156086e-8,
631 | 7.757052846573753e-8,
632 | 8.3828872023569e-8,
633 | 7.996989809787599e-8,
634 | 7.948619141729913e-8,
635 | 7.525599694367098e-8,
636 | 8.488487925296265e-8,
637 | 7.62940864441522e-8,
638 | 7.579118954382819e-8,
639 | 8.418272733283864e-8
640 | ],
641 | "variance": 1.2432246083002961e-17
642 | },
643 | "times": {
644 | "cycle": 0.05518277275580602,
645 | "elapsed": 5.507,
646 | "period": 8.108495677176131e-8,
647 | "timeStamp": 1631437124712
648 | },
649 | "running": false,
650 | "count": 680555,
651 | "cycles": 6,
652 | "hz": 12332743.825896205
653 | },
654 | "name": "(101 bytes)",
655 | "options": {
656 | "name": "shallow"
657 | },
658 | "events": {
659 | "start": [
660 | null
661 | ],
662 | "complete": [
663 | null
664 | ]
665 | },
666 | "length": 1,
667 | "running": false
668 | }
669 | },
670 | "results": [
671 | {
672 | "name": "set-value",
673 | "file": {
674 | "stat": null,
675 | "_contents": null,
676 | "history": [
677 | "/Users/jonschlinkert/dev/js-utils/objects/set-value/benchmark/code/set-value.js"
678 | ],
679 | "_cwd": "/Users/jonschlinkert/dev/js-utils/objects/set-value",
680 | "_isVinyl": true,
681 | "_symlink": null,
682 | "key": "set-value"
683 | },
684 | "runs": 89,
685 | "hz": 12332743.825896205,
686 | "ops": "12,332,744",
687 | "rme": "0.90"
688 | }
689 | ]
690 | }
691 | ]
--------------------------------------------------------------------------------
/benchmark/stats.md:
--------------------------------------------------------------------------------
1 | # deep (194 bytes)
2 | deep-object x 823,287 ops/sec ±1.00% (90 runs sampled)
3 | deep-property x 1,787,990 ops/sec ±0.82% (92 runs sampled)
4 | deephas x 840,700 ops/sec ±0.95% (93 runs sampled)
5 | dot-prop x 1,249,663 ops/sec ±0.89% (90 runs sampled)
6 | dot2val x 2,067,212 ops/sec ±1.08% (91 runs sampled)
7 | es5-dot-prop x 1,668,806 ops/sec ±0.92% (92 runs sampled)
8 | lodash-set x 1,286,725 ops/sec ±0.82% (90 runs sampled)
9 | object-path-set x 1,261,242 ops/sec ±1.63% (90 runs sampled)
10 | object-set x 285,369 ops/sec ±0.91% (90 runs sampled)
11 | set-value x 2,076,931 ops/sec ±0.86% (93 runs sampled)
12 |
13 | fastest is set-value, dot2val (by 203% avg)
14 |
15 | # medium (98 bytes)
16 | deep-object x 5,811,161 ops/sec ±1.12% (90 runs sampled)
17 | deep-property x 4,075,885 ops/sec ±0.91% (90 runs sampled)
18 | deephas x 1,508,136 ops/sec ±0.82% (92 runs sampled)
19 | dot-prop x 2,809,838 ops/sec ±1.16% (87 runs sampled)
20 | dot2val x 4,600,890 ops/sec ±0.76% (91 runs sampled)
21 | es5-dot-prop x 3,263,790 ops/sec ±0.97% (91 runs sampled)
22 | lodash-set x 3,486,628 ops/sec ±1.20% (90 runs sampled)
23 | object-path-set x 3,729,018 ops/sec ±0.90% (92 runs sampled)
24 | object-set x 973,961 ops/sec ±0.80% (92 runs sampled)
25 | set-value x 6,941,474 ops/sec ±1.24% (90 runs sampled)
26 |
27 | fastest is set-value (by 206% avg)
28 |
29 | # shallow (101 bytes)
30 | deep-object x 9,416,410 ops/sec ±1.19% (89 runs sampled)
31 | deep-property x 5,108,536 ops/sec ±0.98% (93 runs sampled)
32 | deephas x 1,706,979 ops/sec ±0.98% (86 runs sampled)
33 | dot-prop x 4,045,902 ops/sec ±1.10% (92 runs sampled)
34 | dot2val x 5,862,418 ops/sec ±0.88% (91 runs sampled)
35 | es5-dot-prop x 4,439,646 ops/sec ±1.18% (90 runs sampled)
36 | lodash-set x 9,303,292 ops/sec ±1.19% (89 runs sampled)
37 | object-path-set x 5,657,479 ops/sec ±0.95% (93 runs sampled)
38 | object-set x 2,020,041 ops/sec ±0.92% (91 runs sampled)
39 | set-value x 11,272,227 ops/sec ±1.36% (88 runs sampled)
40 |
41 | fastest is set-value (by 213% avg)
42 |
--------------------------------------------------------------------------------
/examples.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const set = require('.');
4 |
5 | const data = {};
6 | const key1 = Symbol('key-1');
7 | const key2 = Symbol('key-2');
8 | const key3 = Symbol('key-3');
9 | const key4 = Symbol('key-4');
10 |
11 | set(data, 'a.b.c', true);
12 | set(data, '"a.b".c', true, { split: set.parse });
13 | set(data, 'foo-bar', true);
14 | set(data, ['one', 'two'], true);
15 | set(data, ['one', key1], true);
16 | set(data, [key2, key3, key4], true);
17 | set(data, key1, true);
18 |
19 | console.log(data);
20 |
21 | // console.log(set({}, 'a.b.c', 'd'));
22 | // //=> { a: { b: { c: 'd' } } }
23 |
24 | // console.log(set({}, 'a\\.b.c', 'd'));
25 | // //=> { 'a.b': { c: 'd' } }
26 |
27 | // console.log(set({}, 'a\\.b\\.c', 'd'));
28 | // //=> { 'a.b.c': 'd' }
29 |
30 | // console.log(set({}, '"a.b".c', 'd'));
31 | // //=> { 'a.b': { c: 'd' } }
32 |
33 | // console.log(set({}, "'a.b'.c", "d"));
34 | // //=> { 'a.b': { c: 'd' } }
35 |
36 | // console.log(set({}, "{a..b}.c", "d"));
37 | // //=> { '{a..b}': { c: 'd' } }
38 |
39 | // console.log(set({}, '"this/is/a/.file.path"', 'd'));
40 | // //=> { 'this/is/a/.file.path': 'd' }
41 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * set-value
3 | *
4 | * Copyright (c) Jon Schlinkert (https://github.com/jonschlinkert).
5 | * Released under the MIT License.
6 | */
7 |
8 | 'use strict';
9 |
10 | const { deleteProperty } = Reflect;
11 | const isPrimitive = require('is-primitive');
12 | const isPlainObject = require('is-plain-object');
13 |
14 | const isObject = value => {
15 | return (typeof value === 'object' && value !== null) || typeof value === 'function';
16 | };
17 |
18 | const isUnsafeKey = key => {
19 | return key === '__proto__' || key === 'constructor' || key === 'prototype';
20 | };
21 |
22 | const validateKey = key => {
23 | if (!isPrimitive(key)) {
24 | throw new TypeError('Object keys must be strings or symbols');
25 | }
26 |
27 | if (isUnsafeKey(key)) {
28 | throw new Error(`Cannot set unsafe key: "${key}"`);
29 | }
30 | };
31 |
32 | const toStringKey = input => {
33 | return Array.isArray(input) ? input.flat().map(String).join(',') : input;
34 | };
35 |
36 | const createMemoKey = (input, options) => {
37 | if (typeof input !== 'string' || !options) return input;
38 | let key = input + ';';
39 | if (options.arrays !== undefined) key += `arrays=${options.arrays};`;
40 | if (options.separator !== undefined) key += `separator=${options.separator};`;
41 | if (options.split !== undefined) key += `split=${options.split};`;
42 | if (options.merge !== undefined) key += `merge=${options.merge};`;
43 | if (options.preservePaths !== undefined) key += `preservePaths=${options.preservePaths};`;
44 | return key;
45 | };
46 |
47 | const memoize = (input, options, fn) => {
48 | const key = toStringKey(options ? createMemoKey(input, options) : input);
49 | validateKey(key);
50 |
51 | const value = setValue.cache.get(key) || fn();
52 | setValue.cache.set(key, value);
53 | return value;
54 | };
55 |
56 | const splitString = (input, options = {}) => {
57 | const sep = options.separator || '.';
58 | const preserve = sep === '/' ? false : options.preservePaths;
59 |
60 | if (typeof input === 'string' && preserve !== false && /\//.test(input)) {
61 | return [input];
62 | }
63 |
64 | const parts = [];
65 | let part = '';
66 |
67 | const push = part => {
68 | let number;
69 | if (part.trim() !== '' && Number.isInteger((number = Number(part)))) {
70 | parts.push(number);
71 | } else {
72 | parts.push(part);
73 | }
74 | };
75 |
76 | for (let i = 0; i < input.length; i++) {
77 | const value = input[i];
78 |
79 | if (value === '\\') {
80 | part += input[++i];
81 | continue;
82 | }
83 |
84 | if (value === sep) {
85 | push(part);
86 | part = '';
87 | continue;
88 | }
89 |
90 | part += value;
91 | }
92 |
93 | if (part) {
94 | push(part);
95 | }
96 |
97 | return parts;
98 | };
99 |
100 | const split = (input, options) => {
101 | if (options && typeof options.split === 'function') return options.split(input);
102 | if (typeof input === 'symbol') return [input];
103 | if (Array.isArray(input)) return input;
104 | return memoize(input, options, () => splitString(input, options));
105 | };
106 |
107 | const assignProp = (obj, prop, value, options) => {
108 | validateKey(prop);
109 |
110 | // Delete property when "value" is undefined
111 | if (value === undefined) {
112 | deleteProperty(obj, prop);
113 |
114 | } else if (options && options.merge) {
115 | const merge = options.merge === 'function' ? options.merge : Object.assign;
116 |
117 | // Only merge plain objects
118 | if (merge && isPlainObject(obj[prop]) && isPlainObject(value)) {
119 | obj[prop] = merge(obj[prop], value);
120 | } else {
121 | obj[prop] = value;
122 | }
123 |
124 | } else {
125 | obj[prop] = value;
126 | }
127 |
128 | return obj;
129 | };
130 |
131 | const setValue = (target, path, value, options) => {
132 | if (!path || !isObject(target)) return target;
133 |
134 | const keys = split(path, options);
135 | let obj = target;
136 |
137 | for (let i = 0; i < keys.length; i++) {
138 | const key = keys[i];
139 | const next = keys[i + 1];
140 |
141 | validateKey(key);
142 |
143 | if (next === undefined) {
144 | assignProp(obj, key, value, options);
145 | break;
146 | }
147 |
148 | if (typeof next === 'number' && !Array.isArray(obj[key])) {
149 | obj = obj[key] = [];
150 | continue;
151 | }
152 |
153 | if (!isObject(obj[key])) {
154 | obj[key] = {};
155 | }
156 |
157 | obj = obj[key];
158 | }
159 |
160 | return target;
161 | };
162 |
163 | setValue.split = split;
164 | setValue.cache = new Map();
165 | setValue.clear = () => {
166 | setValue.cache = new Map();
167 | };
168 |
169 | module.exports = setValue;
170 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "set-value",
3 | "version": "4.1.0",
4 | "description": "Set nested properties on an object using dot notation.",
5 | "license": "MIT",
6 | "repository": "jonschlinkert/set-value",
7 | "homepage": "https://github.com/jonschlinkert/set-value",
8 | "bugs": "https://github.com/jonschlinkert/set-value/issues",
9 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
10 | "funding": [
11 | "https://github.com/sponsors/jonschlinkert",
12 | "https://paypal.me/jonathanschlinkert",
13 | "https://jonschlinkert.dev/sponsor"
14 | ],
15 | "contributors": [
16 | "Jon Schlinkert (http://twitter.com/jonschlinkert)",
17 | "(https://github.com/wtgtybhertgeghgtwtg)",
18 | "Vadim Demedes (https://vadimdemedes.com)"
19 | ],
20 | "files": [
21 | "index.js"
22 | ],
23 | "main": "index.js",
24 | "engines": {
25 | "node": ">=11.0"
26 | },
27 | "scripts": {
28 | "test": "mocha"
29 | },
30 | "dependencies": {
31 | "is-plain-object": "^2.0.4",
32 | "is-primitive": "^3.0.1"
33 | },
34 | "devDependencies": {
35 | "gulp-format-md": "^2.0.0",
36 | "mocha": "^9.1.1",
37 | "split-string": "^6.1.0"
38 | },
39 | "keywords": [
40 | "bury",
41 | "deep-get-set",
42 | "deep-object",
43 | "deep-property",
44 | "deep-set-in",
45 | "deep-set",
46 | "deephas",
47 | "dot-prop",
48 | "dot2val",
49 | "es5-dot-prop",
50 | "get",
51 | "getsetdeep",
52 | "has",
53 | "hasown",
54 | "key",
55 | "keys",
56 | "lodash.set",
57 | "nested",
58 | "notation",
59 | "object-path-set",
60 | "object-path",
61 | "object-set",
62 | "object",
63 | "patch",
64 | "prop",
65 | "properties",
66 | "property",
67 | "props",
68 | "put",
69 | "set-deep-prop",
70 | "set-deep",
71 | "set-nested-prop",
72 | "set",
73 | "setvalue",
74 | "split-string",
75 | "value",
76 | "values"
77 | ],
78 | "verb": {
79 | "toc": false,
80 | "layout": "default",
81 | "tasks": [
82 | "readme"
83 | ],
84 | "plugins": [
85 | "gulp-format-md"
86 | ],
87 | "related": {
88 | "list": [
89 | "assign-value",
90 | "get-value",
91 | "has-value",
92 | "merge-value",
93 | "omit-value",
94 | "set-value",
95 | "union-value",
96 | "unset-value"
97 | ]
98 | },
99 | "lint": {
100 | "reflinks": true
101 | },
102 | "reflinks": [
103 | "bury",
104 | "deep-get-set",
105 | "deep-object",
106 | "deep-property",
107 | "deep-set",
108 | "deep-set-in",
109 | "deephas",
110 | "dot-prop",
111 | "dot2val",
112 | "es5-dot-prop",
113 | "getsetdeep",
114 | "lodash.set",
115 | "object-path",
116 | "object-path-set",
117 | "object-set",
118 | "set-deep",
119 | "set-deep-prop",
120 | "set-nested-prop",
121 | "setvalue",
122 | "split-string",
123 | "update"
124 | ]
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/test/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "../.eslintrc.json"
4 | ],
5 | "env": {
6 | "mocha": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('mocha');
4 | const split = require('split-string');
5 | const assert = require('assert');
6 | const set = require('..');
7 |
8 | describe('set-value', () => {
9 | describe('unsafe properties', () => {
10 | it('should not allow setting constructor', () => {
11 | assert.throws(() => set({}, 'a.constructor.b', 'c'));
12 | assert.throws(() => set({}, 'a.constructor', 'c'));
13 | assert.throws(() => set({}, 'constructor', 'c'));
14 | });
15 |
16 | it('should not allow setting prototype', () => {
17 | assert.throws(() => set({}, 'a.prototype.b', 'c'));
18 | assert.throws(() => set({}, 'a.prototype', 'c'));
19 | assert.throws(() => set({}, 'prototype', 'c'));
20 | });
21 |
22 | it('should not allow setting __proto__', () => {
23 | assert.throws(() => set({}, 'a.__proto__.b', 'c'));
24 | assert.throws(() => set({}, 'a.__proto__', 'c'));
25 | assert.throws(() => set({}, '__proto__', 'c'));
26 | });
27 | });
28 |
29 | describe('set', () => {
30 | it('should return non-objects', () => {
31 | const str = set('foo', 'a.b', 'c');
32 | assert.equal(str, 'foo');
33 | const _null = set(null, 'a.b', 'c');
34 | assert.equal(_null, null);
35 | });
36 |
37 | it('should set when key is a symbol', () => {
38 | const key = Symbol('foo');
39 | const obj = {};
40 | set(obj, key, 'bar');
41 | assert.equal(obj[key], 'bar');
42 | });
43 |
44 | it('should set on the root of the object', () => {
45 | const o = {};
46 | set(o, 'foo', 'bar');
47 | assert.equal(o.foo, 'bar');
48 | });
49 |
50 | it('should set the specified property.', () => {
51 | assert.deepEqual(set({ a: 'aaa', b: 'b' }, 'a', 'bbb'), { a: 'bbb', b: 'b' });
52 | });
53 |
54 | it('should set a nested property', () => {
55 | const o = {};
56 | set(o, 'a.b', 'c');
57 | assert.equal(o.a.b, 'c');
58 | });
59 |
60 | it('should set a nested property where the last key is a symbol', () => {
61 | const o = {};
62 | set(o, 'a.b', 'c');
63 | assert.equal(o.a.b, 'c');
64 | });
65 |
66 | it('should support passing an array as the key', () => {
67 | const actual = set({ a: 'a', b: { c: 'd' } }, ['b', 'c', 'd'], 'eee');
68 | assert.deepEqual(actual, { a: 'a', b: { c: { d: 'eee' } } });
69 | });
70 |
71 | it('should set a deeply nested value.', () => {
72 | const actual = set({ a: 'a', b: { c: 'd' } }, 'b.c.d', 'eee');
73 | assert.deepEqual(actual, { a: 'a', b: { c: { d: 'eee' } } });
74 | });
75 |
76 | it('should allow keys to be whitespace', () => {
77 | const o = {};
78 | set(o, 'a. .a', { y: 'z' });
79 | assert.deepEqual(o.a[' '].a, { y: 'z' });
80 | });
81 |
82 | it('should extend an array', () => {
83 | const o = { a: [] };
84 | set(o, 'a.0', { y: 'z' });
85 | assert(Array.isArray(o.a));
86 | assert.deepEqual(o.a[0], { y: 'z' });
87 | });
88 |
89 | it('should create an array if it does not already exist', () => {
90 | const o = {};
91 | set(o, 'a.0.a', { y: 'z' });
92 | set(o, 'a.1.b', { y: 'z' });
93 | set(o, 'a.2.c', { y: 'z' });
94 | set(o, 'b.0', { y: 'z' });
95 | set(o, '0', { y: 'z' });
96 | assert(Array.isArray(o.a));
97 | assert.deepEqual(o.a[0].a, { y: 'z' });
98 | assert.deepEqual(o.a[1].b, { y: 'z' });
99 | assert.deepEqual(o.a[2].c, { y: 'z' });
100 | assert.deepEqual(o.b, [{ y: 'z' }]);
101 | assert.deepEqual(o['0'], { y: 'z' });
102 | });
103 |
104 | it('should extend a function', () => {
105 | const log = () => {};
106 | const warning = () => {};
107 | const o = {};
108 |
109 | set(o, 'helpers.foo', log);
110 | set(o, 'helpers.foo.warning', warning);
111 | assert.equal(typeof o.helpers.foo, 'function');
112 | assert.equal(typeof o.helpers.foo.warning, 'function');
113 | });
114 |
115 | it('should extend an object in an array', () => {
116 | const o = { a: [{}, {}, {}] };
117 | set(o, 'a.0.a', { y: 'z' });
118 | set(o, 'a.1.b', { y: 'z' });
119 | set(o, 'a.2.c', { y: 'z' });
120 | assert(Array.isArray(o.a));
121 | assert.deepEqual(o.a[0].a, { y: 'z' });
122 | assert.deepEqual(o.a[1].b, { y: 'z' });
123 | assert.deepEqual(o.a[2].c, { y: 'z' });
124 | });
125 |
126 | it('should create a deeply nested property if it does not already exist', () => {
127 | const o = {};
128 | set(o, 'a.b.c.d.e', 'c');
129 | assert.equal(o.a.b.c.d.e, 'c');
130 | });
131 |
132 | it('should not create a nested property if it does already exist', () => {
133 | const first = { name: 'Halle' };
134 | const o = { a: first };
135 | set(o, 'a.b', 'c');
136 | assert.equal(o.a.b, 'c');
137 | assert.equal(o.a, first);
138 | assert.equal(o.a.name, 'Halle');
139 | });
140 |
141 | it('should support immediate properties', () => {
142 | const o = {};
143 | set(o, 'a', 'b');
144 | assert.equal(o.a, 'b');
145 | });
146 |
147 | it('should use property paths to set nested values from the source object.', () => {
148 | const o = {};
149 | set(o, 'a.locals.name', { first: 'Brian' });
150 | set(o, 'b.locals.name', { last: 'Woodward' });
151 | set(o, 'b.locals.name.last', 'Woodward');
152 | assert.deepEqual(o, { a: { locals: { name: { first: 'Brian' } } }, b: { locals: { name: { last: 'Woodward' } } } });
153 | });
154 |
155 | it('should delete the property when value is undefined', () => {
156 | const fixture = {};
157 | assert.deepEqual(set(fixture, 'a.locals.name'), { a: { locals: {} } });
158 | assert.deepEqual(set(fixture, 'b.locals.name'), { b: { locals: {} }, a: { locals: {} } });
159 | assert.deepEqual(set({ a: 'a', b: { c: 'd' } }, 'b.c'), { a: 'a', b: {} });
160 | });
161 |
162 | it('should return the entire object if no property is passed.', () => {
163 | assert.deepEqual(set({ a: 'a', b: { c: 'd' } }), { a: 'a', b: { c: 'd' } });
164 | });
165 |
166 | it('should set non-plain objects', done => {
167 | const o = {};
168 |
169 | set(o, 'a.b', new Date());
170 | const firstDate = o.a.b.getTime();
171 |
172 | setTimeout(function() {
173 | set(o, 'a.b', new Date());
174 | const secondDate = o.a.b.getTime();
175 |
176 | assert.notDeepEqual(firstDate, secondDate);
177 | done();
178 | }, 10);
179 | });
180 | });
181 |
182 | describe('escaping', () => {
183 | it('should not split escaped dots', () => {
184 | const o = {};
185 | set(o, 'a\\.b.c.d.e', 'c');
186 | assert.equal(o['a.b'].c.d.e, 'c');
187 | });
188 |
189 | it('should support multiple escaped dots', () => {
190 | const obj1 = {};
191 | set(obj1, 'e\\.f\\.g', 1);
192 | assert.equal(obj1['e.f.g'], 1);
193 |
194 | const obj2 = {};
195 | set(obj2, 'e\\.f.g\\.h\\.i.j', 1);
196 | assert.deepEqual(obj2, { 'e.f': { 'g.h.i': { j: 1 } } });
197 | });
198 |
199 | it('should support multiple escaped dots', () => {
200 | const obj1 = {};
201 | const key = Symbol('key');
202 | set(obj1, [key, 'e.f', 'g'], 1);
203 | assert.equal(obj1[key]['e.f'].g, 1);
204 |
205 | const obj2 = {};
206 | set(obj2, 'e\\.f.g\\.h\\.i.j', 1);
207 | assert.deepEqual(obj2, { 'e.f': { 'g.h.i': { j: 1 } } });
208 | });
209 |
210 | it('should correctly parse multiple consecutive backslashes', () => {
211 | assert.deepEqual(set.split('a.b.c'), ['a', 'b', 'c']);
212 | assert.deepEqual(set.split('b\\.c\\.d'), ['b.c.d']);
213 | assert.deepEqual(set.split('b\\\\.c\\.d'), ['b\\', 'c.d']);
214 | assert.deepEqual(set.split('a.b\\.c'), ['a', 'b.c']);
215 | assert.deepEqual(set.split('a.b\\\\.c'), ['a', 'b\\', 'c']);
216 | assert.deepEqual(set.split('a.b\\\\\\.c'), ['a', 'b\\.c']);
217 | });
218 | });
219 |
220 | describe('options.merge', () => {
221 | it('should merge an existing value with the given value', () => {
222 | const o = { a: { b: { c: 'd' } } };
223 | set(o, 'a.b', { y: 'z' }, { merge: true });
224 | assert.deepEqual(o.a.b, { c: 'd', y: 'z' });
225 |
226 | const obj = { foo: { bar: { baz: 'qux' } } };
227 | set(obj, 'foo.bar.fez', 'zzz', { merge: true });
228 | assert.deepEqual(obj, { foo: { bar: { baz: 'qux', fez: 'zzz' } } });
229 | });
230 |
231 | it('should update an object by merging values', () => {
232 | const o = {};
233 | set(o, 'a', { b: 'c' });
234 | set(o, 'a', { c: 'd' }, { merge: true });
235 | assert.deepEqual(o, { a: { b: 'c', c: 'd' } });
236 | set(o, 'a', 'b');
237 | assert.equal(o.a, 'b');
238 | });
239 | });
240 |
241 | describe('options.preservePaths', () => {
242 | it('should split properties with a forward slash when preservePaths is false', () => {
243 | const obj = {};
244 | set(obj, 'https://github.com', true, { preservePaths: false });
245 |
246 | assert.deepEqual(obj, { 'https://github': { com: true } });
247 | });
248 |
249 | it('should not split properties with a forward slash', () => {
250 | const obj = {};
251 | set(obj, 'foo/bar/baz.md', 'c');
252 | assert.equal(obj['foo/bar/baz.md'], 'c');
253 | });
254 | });
255 |
256 | describe('options.split', () => {
257 | const keep = (value, state) => {
258 | return value !== '"' && value !== '\'';
259 | };
260 |
261 | const options = {
262 | split(prop) {
263 | return split(prop, { separator: '.', brackets: true, quotes: true, keep });
264 | }
265 | };
266 |
267 | it('should use simple String.split() when options.split is not defined', () => {
268 | const o = {};
269 | set(o, 'a."b.c.d".e', 'c');
270 | assert.equal(o.a['"b'].c['d"'].e, 'c');
271 | });
272 |
273 | it('should take a custom separator', () => {
274 | const o = {};
275 | set(o, 'a/b/c/d/e', 'c', { separator: '/' });
276 | assert.equal(o.a.b.c.d.e, 'c');
277 | });
278 |
279 | it('should use a custom function to not split inside double quotes', () => {
280 | const o = {};
281 | set(o, 'a."b.c.d".e', 'c', options);
282 | assert.equal(o.a['b.c.d'].e, 'c');
283 | });
284 |
285 | it('should use a custom function to not split inside single quotes', () => {
286 | const o = {};
287 | set(o, "a.'b.c.d'.e", 'c', options);
288 | assert.equal(o.a['b.c.d'].e, 'c');
289 | });
290 |
291 | it('should use a custom function to not split inside square brackets', () => {
292 | const o = {};
293 | set(o, 'a.[b.c.d].e', 'c', options);
294 | assert.equal(o.a['[b.c.d]'].e, 'c');
295 | });
296 |
297 | it('should use a custom function to not split inside parens', () => {
298 | const o = {};
299 | set(o, 'a.(b.c.d).e', 'c', options);
300 | assert.equal(o.a['(b.c.d)'].e, 'c');
301 | });
302 |
303 | it('should use a custom function to not split inside angle brackets', () => {
304 | const o = {};
305 | set(o, 'a..e', 'c', options);
306 | assert.equal(o.a[''].e, 'c');
307 | });
308 |
309 | it('should use a custom function to not split inside curly braces', () => {
310 | const o = {};
311 | set(o, 'a.{b.c.d }.e', 'c', options);
312 | assert.equal(o.a['{b.c.d }'].e, 'c');
313 | });
314 | });
315 | });
316 |
--------------------------------------------------------------------------------