diff --git a/CHANGELOG.md b/CHANGELOG.md index fb05154..9212380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Change Log ## [unreleased] +* Add support for wildcards (`*`) to `u.updateIn`. (https://github.com/substantial/updeep/issues/27) ## [0.9.0] * Do not add object during false branch of `u.if`. diff --git a/README.md b/README.md index 2fd380d..35bd80a 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ expect(result).to.eql({ name: "Joe Merrill", age: 22 }); ### `u.updateIn(path(, value)(, object))` -Update a single value with a simple string or array path. Can be use to update nested objects, arrays, or a combination. +Update a single value with a simple string or array path. Can be use to update nested objects, arrays, or a combination. Can also be used to update every element of a nested array with `'*'`. ```js var result = u.updateIn('bunny.color', 'brown', { bunny: { color: 'black' } }); @@ -214,6 +214,22 @@ var result = u({ pets: u.updateIn([0, 'bunny', 'age'], 3) }, { pets: [{ bunny: { expect(result).to.eql({ pets: [{ bunny: { age: 3 } }] }); ``` +```js +var result = u.updateIn('todos.*.done', true, { + todos: [ + { done: false }, + { done: false }, + ] +}); + +expect(result).to.eql({ + todos: [ + { done: true }, + { done: true }, + ] +}); +``` + ### `u.constant(object)` Sometimes, you want to replace an object outright rather than merging it. diff --git a/lib/updateIn.js b/lib/updateIn.js index 72a9d71..8b388c9 100644 --- a/lib/updateIn.js +++ b/lib/updateIn.js @@ -1,10 +1,26 @@ import curry from './util/curry'; import update from './update'; +import map from './map'; import splitPath from './util/splitPath'; +const wildcard = '*'; + +function reducePath(acc, key) { + if (key === wildcard) { + return value => + Object.prototype.hasOwnProperty.call(value, wildcard) ? + // If we actually have wildcard as a property, update that + update({ [wildcard]: acc }, value) : + // Otherwise map over all properties + map(acc, value); + } + + return { [key]: acc }; +} + function updateIn(path, value, object) { const parts = splitPath(path); - const updates = parts.reduceRight((acc, key) => ({ [key]: acc }), value); + const updates = parts.reduceRight(reducePath, value); return update(updates, object); } diff --git a/test/updateIn-spec.js b/test/updateIn-spec.js index 6f9b73a..b7c9cc4 100644 --- a/test/updateIn-spec.js +++ b/test/updateIn-spec.js @@ -48,4 +48,20 @@ describe('u.updateIn', () => { it('freezes the result', () => { expect(Object.isFrozen(u.updateIn('a', 0, {}))).to.be.true; }); + + it('can multiple elements of an array with *', () => { + let object = { a: [{ b: 0 }, { b: 1 }, { b: 2 }] }; + let result = u.updateIn('a.*.b', x => x + 1, object); + expect(result).to.eql({ a: [{ b: 1 }, { b: 2 }, { b: 3 }] }); + + object = { a: [0, 1, 2] }; + result = u.updateIn(['a', '*'], x => x + 1, object); + expect(result).to.eql({ a: [1, 2, 3] }); + }); + + it('can update properties named *', () => { + const object = { '*': 1, x: 1 }; + const result = u.updateIn('*', x => x + 1, object); + expect(result).to.eql({ '*': 2, x: 1 }); + }); });