diff --git a/CHANGELOG.md b/CHANGELOG.md
index f565e34c..10e36e8f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,9 @@
-# 1.34.1
+# 1.35.0
+- Add tree `treeLookup`
+- Add deep path support to `lensProp`
+- Add `unsetOn`
+
+# 1.34.1
- Ignore browser testing errors.
- Add karma JSON reporter.
- Only watch files and record videos/screenshots for the local test.
diff --git a/README.md b/README.md
index f75ed3f9..49d853f1 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
---
@@ -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 97384eaf..38db394d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "futil-js",
- "version": "1.34.1",
+ "version": "1.35.0",
"description": "F(unctional) util(ities). Resistance is futile.",
"main": "lib/futil-js.js",
"scripts": {
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']
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/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/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,
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])
+ })
})