From bcd23eac094cda731c0e9e67884c196772556d1b Mon Sep 17 00:00:00 2001 From: Samuel Greene Date: Sat, 28 Oct 2017 05:50:33 +0200 Subject: [PATCH 1/4] lensProp deep path support --- src/lens.js | 6 ++++-- test/lens.spec.js | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lens.js b/src/lens.js index 14e0b7c6..db1ea2ff 100644 --- a/src/lens.js +++ b/src/lens.js @@ -1,4 +1,5 @@ import _ from 'lodash/fp' +import {setOn} from './conversion' // Stubs export let functionLens = val => (...x) => { @@ -22,9 +23,10 @@ export let objToFn = lens => (...values) => // Lens Construction export let lensProp = (field, source) => ({ - get: () => source[field], + get: () => _.get(field, source),//source[field], set: value => { - source[field] = value + setOn(field, value, source) + // source[field] = value }, }) diff --git a/test/lens.spec.js b/test/lens.spec.js index b99cd5d4..1db58d4f 100644 --- a/test/lens.spec.js +++ b/test/lens.spec.js @@ -42,6 +42,16 @@ describe('Lens Functions', () => { l.set(5) expect(l.get()).to.equal(5) }) + it('lensProp deep', () => { + let l = f.lensProp('x.a', { + x: { + a: 1 + }, + }) + expect(l.get()).to.equal(1) + l.set(5) + expect(l.get()).to.equal(5) + }) it('lensOf', () => { let l = f.lensOf({ a: 1, From 52fa057c9883efcd1d9d926ec7c3ac935a0e073e Mon Sep 17 00:00:00 2001 From: Samuel Greene Date: Sat, 28 Oct 2017 05:50:46 +0200 Subject: [PATCH 2/4] add tree lookup method --- src/tree.js | 7 ++++++- test/tree.spec.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/tree.js b/src/tree.js index 9f9aac1c..42b11464 100644 --- a/src/tree.js +++ b/src/tree.js @@ -33,10 +33,15 @@ export let treeToArray = (next = traverse) => treeToArrayBy(next)(x => x) export let leaves = (next = traverse) => _.flow(treeToArray(next), _.reject(next)) -export let tree = (next = traverse) => ({ +export let treeLookup = (next = traverse, buildIteratee = _.identity) => (path, tree) => + _.reduce((tree, path) => _.find(buildIteratee(path), next(tree)), tree, path) + + +export let tree = (next = traverse, buildIteratee = _.identity) => ({ walk: walk(next), reduce: reduceTree(next), toArrayBy: treeToArrayBy(next), toArray: treeToArray(next), leaves: leaves(next), + lookup: treeLookup(next, buildIteratee) }) diff --git a/test/tree.spec.js b/test/tree.spec.js index 64f612b0..4fbab223 100644 --- a/test/tree.spec.js +++ b/test/tree.spec.js @@ -131,4 +131,41 @@ describe('Tree Functions', () => { let tree = f.tree() expect(tree.toArray(x)).to.deep.equal([x, x.a, x.b, x.b.c]) }) + it('lookup', () => { + let x = { + a: 1, + items: [{ + a: 2, + items: [{ + a: 3 + }, { + a: 4, + b: 4 + }] + }, { + a: 5 + }] + } + let tree = f.tree(x => x.items) + + expect(tree.lookup([{a:2}, {a:4}], x)).to.deep.equal(x.items[0].items[1]) + }) + it('lookup with path', () => { + let x = { + a: '1', + items: [{ + a: '2', + items: [{ + a: '3' + }, { + a: '4', + b: 4 + }] + }, { + a: '5' + }] + } + let tree = f.tree(x => x.items, a => ({a})) + expect(tree.lookup(['2', '4'], x)).to.deep.equal(x.items[0].items[1]) + }) }) From 0426e0c89192c817997857a0182ffeaf283d0ef5 Mon Sep 17 00:00:00 2001 From: Samuel Greene Date: Sat, 28 Oct 2017 06:04:24 +0200 Subject: [PATCH 3/4] add unsetOn --- src/conversion.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conversion.js b/src/conversion.js index f39a74e2..c8a9886f 100644 --- a/src/conversion.js +++ b/src/conversion.js @@ -18,6 +18,8 @@ export const extendOn = _.extend.convert(mutable) export const defaultsOn = _.defaults.convert(mutable) export const mergeOn = _.merge.convert(mutable) export const setOn = _.set.convert(mutable) +// Curry required until https://github.com/lodash/lodash/issues/3440 is resolved +export let unsetOn = _.curryN(2, _.unset.convert({immutable: false})) // This reduce based version is easier to maintain but requires calling `F.inversions.fn` instead of `F.fn` const inversionList = ['get', 'pick', 'includes'] From 7a79ddc1ce9e52cb46efcb2d5c838d6134702bd2 Mon Sep 17 00:00:00 2001 From: Samuel Greene Date: Sat, 28 Oct 2017 06:05:01 +0200 Subject: [PATCH 4/4] release prep --- CHANGELOG.md | 7 ++++++- README.md | 14 +++++++++----- package.json | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 915a6ce2..3b24fb50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# 1.34.0 +# 1.35.0 +- Add tree `treeLookup` +- Add deep path support to `lensProp` +- Ass `unsetOn` + +# 1.34.0 - Fixed flattenObject and diffArray to properly say the paths of arrays with only one object, by making them use a new function: `dotJoinWith`, which is like `dotJoin` but allows you to provide a diff --git a/README.md b/README.md index f75ed3f9..49d853f1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -futil-js +futil-js --- @@ -111,7 +111,7 @@ These methods provide alternative orderings that are sometimes more convenient. The idea of `In` methods is to name them by convention, so when ever you need a method that actually takes the collection first (e.g. a `get` where the data is static but the field is dynamic), you can just add `In` to the end (such as `getIn` which takes the object first) ### `On`s (Immutable False) -`extendOn`, `defaultsOn`, `mergeOn`, `setOn` +`extendOn`, `defaultsOn`, `mergeOn`, `setOn`, `unsetOn` lodash/fp likes to keep things pure, but sometimes JS can get pretty dirty. These methods are alternatives for working with data that--for whatever the use case is--needs to be mutable Any methods that interact with mutable data will use the `On` convention (as it is some action occuring `On` some data) @@ -133,7 +133,7 @@ Any method with uncapped iteratee arguments will use the `Indexed` convention. ### dotJoinWith `filterFunction -> data:array -> result:string` Compacts an array by the provided function, then joins it with '.' - + ### repeated `data:array -> result:array` Returns an array of elements that are repeated in the array. @@ -391,7 +391,7 @@ This the first main way you'll generally interact with the lens API #### lensProp `lensProp :: string -> object -> { get: () -> T, set: T -> T }` -Creates an object lens for a given property on an object. `.get` returns the value at that path and `set` places a new value at that path +Creates an object lens for a given property on an object. `.get` returns the value at that path and `set` places a new value at that path. Supports deep paths like lodash get/set. #### lensOf @@ -531,6 +531,10 @@ Like `treeToArray`, but accepts a customizer to process the tree nodes before pu `traverse -> tree -> [treeNodes]` Returns an array of the tree nodes that can't be traversed into in `pre-order`. +### treeLookup +`(traverse, buildIteratee) -> ([path], tree) -> treeNode` +Looks up a node matching a path, which defaults to lodash `iteratee` but can be customized with buildIteratee. + ### tree -`traverse -> {walk, reduce, toArray, toArrayBy, leaves}` +`(traverse, buildIteratee) -> {walk, reduce, toArray, toArrayBy, leaves, lookup}` Takes a traversal function and returns an object with all of the tree methods pre-applied with the traversal. This is useful if you want to use a few of the tree methods with a custom traversal and can provides a slightly nicer api. diff --git a/package.json b/package.json index 8a37fa65..d37779ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "futil-js", - "version": "1.34.0", + "version": "1.35.0", "description": "F(unctional) util(ities). Resistance is futile.", "main": "lib/futil-js.js", "scripts": {