From 2ae6f0a11307ede2b634d24dc97995fd798319ac Mon Sep 17 00:00:00 2001 From: Aaron Jensen Date: Tue, 4 Aug 2015 22:28:31 -0700 Subject: [PATCH] Add u.in and rework readme --- CHANGELOG.md | 1 + README.md | 69 ++++++++++++++++++++++++++++++++++----------- lib/updateIn.js | 11 ++++++++ test/freeze-spec.js | 2 +- test/in-spec.js | 35 +++++++++++++++++++++++ 5 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 lib/updateIn.js create mode 100644 test/in-spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 419bf18..e1ddb20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [unreleased] * Add `u.freeze` to freeze an object deeply. (https://github.com/substantial/updeep/issues/7) +* Add `u.in` to update a single value in an object with a specified path. (https://github.com/substantial/updeep/issues/6) ## [0.2.3] * Fix cannot update value to null (https://github.com/substantial/updeep/issues/8) diff --git a/README.md b/README.md index 7b5084e..47900bf 100644 --- a/README.md +++ b/README.md @@ -64,21 +64,25 @@ var newPerson = u({ // } ``` -### Simple update +**NOTE**: All functions are curried, so if you see `f(x(, y))`, it can be called with either `f(x, y)` or `f(x)(y)`. + +### `u(updates(, object))` + +#### Simple update ```js u({ x: { b: 3 } }, { x: { a: 0, b: 0 } }); // => { x: { a: 0, b: 3 } } ``` -### Multiple updates, including an array +#### Multiple updates, including an array ```js u({ x: { b: 3 }, y: { 1: 4 } }, { x: { a: 0, b: 0 }, y: [0, 0] }); // => { x: { a: 0, b: 3 }, y: [0, 4] } ``` -### Use a function +#### Use a function ```js function inc(i) { return i + 1; } @@ -86,7 +90,7 @@ u({ x: { b: inc } }, { x: { a: 0, b: 0 } }); // => { x: { a: 0, b: 1 } } ``` -### Partial application +#### Partial application ```js var setBTo3 = u({ b: 3 }); @@ -94,14 +98,53 @@ setBTo3({ a: 0, b: 0 }); // => { a: 0, b: 3 }) ``` -### Remove a property +#### ES6 computed properties ```js -u({ x: u.omit('b') }, { x: { a: 0, b: 0 } }); +var key = 'b'; +u({ x: { [key]: 3 } }, { x: { a: 0, b: 0 } }); +// => { x: { a: 0, b: 3 } } +``` + +### `u.in(path(, value)(, object))` + +Update a single value with a simple string or array path. + +```js +u.in('a.b', 3, { a: { b: 0 } }); +// => { a: { b: 3 } }; +``` + +```js +function inc(i) { return i + 1; } +u.in('a.b', inc, { a: { b: 0 } }); +// => { a: { b: 1 } }; +``` + +```js +u({ + x: u.in(['a', 'b'], 3) +}, { x: { a: { b: 0 } } }); +// => { x: { a: { b: 3 } } }; +``` + +### `u.omit(predicate(, object))` + +Remove properties. See [`_.omit`](https://lodash.com/docs#omit). + +```js +u({ x: u.omit('b') }, { x: { a: 0, b: 0, c: 0 } }); +// => { x: { a: 0, c: 0 } } +``` + +```js +u({ x: u.omit(['b', 'c']) }, { x: { a: 0, b: 0, c: 0 } }); // => { x: { a: 0 } } ``` -### Reject an item from an array +### `u.reject(predicate(, object))` + +Reject items from an array. See [`_.reject`](https://lodash.com/docs#reject). ```js function even(i) { return i % 2 === 0 }; @@ -109,21 +152,15 @@ u({ x: u.reject(even) }, { x: [1, 2, 3, 4] }); // => { x: [1, 3] } ``` -### With a default +### `u.withDefault(default(, updates)(, object))` + +Like `u()`, but start with the default value if the original value is undefined. ```js u({ x: withDefault([], { 0: 3 }) }, {}); // => { x: [3] } ``` -### ES6 computed properties - -```js -var key = 'b'; -u({ x: { [key]: 3 } }, { x: { a: 0, b: 0 } }); -// => { x: { a: 0, b: 3 } } -``` - See the [tests] for more examples. ## Install diff --git a/lib/updateIn.js b/lib/updateIn.js new file mode 100644 index 0000000..05dbc09 --- /dev/null +++ b/lib/updateIn.js @@ -0,0 +1,11 @@ +import curry from 'lodash/function/curry'; +import update from './update'; + +function updateIn(path, value, obj) { + const parts = Array.isArray(path) ? path : path.split('.'); + const updates = parts.reduceRight((acc, key) => ({ [key]: acc }), value); + + return update(updates, obj); +} + +export default curry(updateIn); diff --git a/test/freeze-spec.js b/test/freeze-spec.js index 6c89d22..1d84b6a 100644 --- a/test/freeze-spec.js +++ b/test/freeze-spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import freeze from '../lib/freeze'; -describe('freeze', () => { +describe('u.freeze', () => { afterEach(() => { delete process.env.NODE_ENV; }); diff --git a/test/in-spec.js b/test/in-spec.js new file mode 100644 index 0000000..96cc4cb --- /dev/null +++ b/test/in-spec.js @@ -0,0 +1,35 @@ +import { expect } from 'chai'; +import updateIn from '../lib/updateIn'; + +describe('u.in', () => { + it('can update a single path described with a string', () => { + const obj = { a: { b: 0 } }; + const result = updateIn('a.b', 3, obj); + expect(result).to.eql({ a: { b: 3 } }); + }); + + it('can update a single path described with a string with a function', () => { + const inc = x => x + 1; + const obj = { a: { b: 0 } }; + const result = updateIn('a.b', inc, obj); + expect(result).to.eql({ a: { b: 1 } }); + }); + + it('can update a single path described with an array', () => { + const obj = { a: { b: 0 } }; + const result = updateIn(['a', 'b'], 3, obj); + expect(result).to.eql({ a: { b: 3 } }); + }); + + it('can update arrays', () => { + const obj = { a: [0, 0, 0] }; + const result = updateIn('a.1', 3, obj); + expect(result).to.eql({ a: [0, 3, 0] }); + }); + + it('can be partially applied', () => { + const obj = { a: { b: 0 } }; + const result = updateIn('a.b')(3)(obj); + expect(result).to.eql({ a: { b: 3 } }); + }); +});