├── .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 [![NPM version](https://img.shields.io/npm/v/set-value.svg?style=flat)](https://www.npmjs.com/package/set-value) [![NPM monthly downloads](https://img.shields.io/npm/dm/set-value.svg?style=flat)](https://npmjs.org/package/set-value) [![NPM total downloads](https://img.shields.io/npm/dt/set-value.svg?style=flat)](https://npmjs.org/package/set-value) [![Tests](https://github.com/jonschlinkert/set-value/actions/workflows/main.yml/badge.svg)](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 | --------------------------------------------------------------------------------