Skip to content

Commit

Permalink
deepFind (and deepTransform)
Browse files Browse the repository at this point in the history
deepTransform
Transforms a recursive iterable by a given function. Works like deepMap but
allows you to stop transforming at any point of the tree. The traversing
happens depth first.

deepFind
Goes over every `{ key, value }` of a recursive iterable data structure (for
arrays being numeric key and the value at the key position), and uses a function to extract
elements at any point in the tree onto an array that is returned. If a third
argument, the limit, is provided, it will stop iterating as soon as the number
of found objects reaches that limit.

Why?
Because this will allow me to find the first two nested occurrences of a terms
object with a field property and to modify the first so that it has:
`collect_mode: 'breadth_first'`
  • Loading branch information
sadasant committed Mar 4, 2017
1 parent 85aa9a4 commit a92f89f
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 1 deletion.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,17 @@ Maps a function over a recursive iterable. Works by default for nested Arrays, n
nested Arrays and Plain Objects. Also works for any other iterable data type as long as
two other values are sent: a mapping function, and a type checker (See the
unit tests for deepMap).

## deepTransform
`deepTransform :: (a -> b) -> [a] -> [b]`
Transforms a recursive iterable by a given function. Works like deepMap but
allows you to stop transforming at any point of the tree. The traversing
happens depth first.

## deepFind
`deepFind :: (a -> b) -> [a] -> [b]`
Goes over every `{ key, value }` of a recursive iterable data structure (for
arrays being numeric key and the value at the key position), and uses a function to extract
elements at any point in the tree onto an array that is returned. If a third
argument, the limit, is provided, it will stop iterating as soon as the number
of found objects reaches that limit.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "futil-js",
"version": "1.8.3",
"version": "1.9.0",
"description": "F(unctional) util(ities). Resistance is futile.",
"main": "lib/futil-js.js",
"scripts": {
Expand Down
18 changes: 18 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ export const map = _.curry((f, x) => (_.isArray(x) ? _.map : _.mapValues)(f, x))
export const deepMap = _.curry((fn, obj, _map = map, is = isTraversable) =>
_map(e => is(e) ? deepMap(fn, fn(e), _map, is) : e, obj))

// Trasnform any recursive algebraic datastructure
export const deepTransform = fn => obj => _.transform((result, pair) => {
let { 0: key, 1: value, bool } = pair
if (!fn(result, value, key)) return false
result.push(_.flatten(deepTransform(_.flow(fn, x => (bool = x)))(value)))
return bool
}, [], _.toPairs(obj))

// Finds matching keys or values in recursively nested datastructures
export const deepFind = (fn, obj, limit = Infinity, skip = 0) =>
_.flatten(deepTransform((obj, value, key) => {
fn(key, value) && obj.push({ [key]: value }) && skip++
return skip < limit
})(obj))

// Queries a traversable data structure
// query(fn, limit, obj)

// Misc
// ----
export const testRegex = regex => regex.test.bind(regex)
28 changes: 28 additions & 0 deletions test/algebras.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,32 @@ describe('Algebras', () => {

expect(JSON.stringify(setMutated)).to.equal('[0,[1,[2,101],101]]')
})

it('deepFind', () => {
const target = {
N1: [ {
N2: [ { N21: [ 210 ], N22: [ 220 ] } ],
N4: [ { N41: [ 410 ], N42: [ 420 ] } ],
N6: [ { N61: [ 610 ], N62: [ 620 ] } ],
N8: [ { N81: [ 810 ], N82: [ 820 ] } ]
}, {
N3: [ { N31: [ 310 ], N32: [ 320 ] } ],
N5: [ { N51: [ 510 ], N52: [ 520 ] } ],
N7: [ { N71: [ 710 ], N72: [ 720 ] } ],
N9: [ { N91: [ 910 ], N92: [ 920 ] } ]
} ]
}

const keyToInt = k => parseInt(k.slice(1))
const isPair = v => v % 2 === 0
const keysToNumbers = keys => _.map(_.flow(_.keys, _.head, keyToInt), keys)

const pairKeys = keysToNumbers(f.deepFind(_.flow(keyToInt, isPair), target))

expect(pairKeys).to.deep.equal([ 2, 22, 4, 42, 6, 62, 8, 82, 32, 52, 72, 92 ])

const twoPairKeys = keysToNumbers(f.deepFind(_.flow(keyToInt, isPair), target, 4))

expect(twoPairKeys).to.deep.equal([ 2, 22, 4, 42 ])
})
})

0 comments on commit a92f89f

Please sign in to comment.