From f412a55906a3623205eb51e47a0a63456da24a10 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Date: Wed, 7 Feb 2024 15:47:22 -0500 Subject: [PATCH 1/2] Do not remove zeroes from tree paths --- .changeset/big-plums-wave.md | 5 +++++ src/array.js | 22 +++++++++++++++++++++- src/collection.js | 1 + src/string.js | 2 ++ src/tree.js | 6 +++--- test/tree.spec.js | 6 ++++++ 6 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 .changeset/big-plums-wave.md diff --git a/.changeset/big-plums-wave.md b/.changeset/big-plums-wave.md new file mode 100644 index 00000000..573e3a41 --- /dev/null +++ b/.changeset/big-plums-wave.md @@ -0,0 +1,5 @@ +--- +'futil': patch +--- + +Do not remove zeroes from tree paths diff --git a/src/array.js b/src/array.js index e85e264b..7a6aaa9b 100644 --- a/src/array.js +++ b/src/array.js @@ -18,7 +18,9 @@ let last = _.takeRight(1) * @signature joinString -> [string1, string2, ...stringN] -> string1 + joinString + string2 + joinString ... + stringN * @typescript {(join: string, x: any[]) => string} */ -export let compactJoin = _.curry((join, x) => _.compact(x).join(join)) +export let compactJoin = + // TODO: (major) Do not use `_.compact` as it removes the number 0. + _.curry((join, x) => _.compact(x).join(join)) /** * Compacts and joins an array with `.` @@ -171,6 +173,24 @@ export let dotEncoder = encoder('.') */ export let slashEncoder = encoder('/') +// The name "pack" is tentative. It's intended to behave as `_.compact` without +// removing the number 0. +let pack = _.remove( + (x) => x === undefined || x === null || x === false || x === '' +) + +// Internal use until we release next major +let __internalEncoder = (separator) => ({ + encode: _.flow(pack, _.join(separator)), + decode: _.split(separator), +}) + +// Internal use until we release next major +export let __internalDotEncoder = __internalEncoder('.') + +// Internal use until we release next major +export let __internalSlashEncoder = __internalEncoder('/') + /** * Takes a predicate function and an array, and returns an array of arrays where each element has one or more elements of the original array. Similar to Haskell's [groupBy](http://zvon.org/other/haskell/Outputlist/groupBy_f.html). diff --git a/src/collection.js b/src/collection.js index 5ffb5962..523362af 100644 --- a/src/collection.js +++ b/src/collection.js @@ -59,6 +59,7 @@ export let insertAtIndex = _.curry((index, val, collection) => * @signature (fn, collection) -> collection */ export let compactMap = _.curry((fn, collection) => + // TODO: (major) Do not use `_.compact` as it removes the number 0. _.flow(_.map(fn), _.compact)(collection) ) diff --git a/src/string.js b/src/string.js index ad4c02da..4f342ed1 100644 --- a/src/string.js +++ b/src/string.js @@ -27,6 +27,8 @@ export const quote = wrap('"', '"') * @signature 'asdf' -> '(asdf)' */ export const parens = wrap('(', ')') + +// TODO: (major) Do not use `_.compact` as it removes the number 0. export const concatStrings = _.flow(_.compact, _.map(_.trim), _.join(' ')) /** diff --git a/src/tree.js b/src/tree.js index c1b27295..2498118d 100644 --- a/src/tree.js +++ b/src/tree.js @@ -7,7 +7,7 @@ import _ from 'lodash/fp' import { findIndexed } from './conversion' -import { pushOn, dotEncoder, slashEncoder } from './array' +import { pushOn, __internalDotEncoder, __internalSlashEncoder } from './array' /** * A default check if something can be traversed - currently it is arrays and plain objects. @@ -239,7 +239,7 @@ export let treeValues = (x, i, xs) => [x, ...xs] * @signature (build, encoder) -> treePathBuilderFunction */ export let treePath = - (build = treeKeys, encoder = dotEncoder) => + (build = treeKeys, encoder = __internalDotEncoder) => (...args) => (encoder.encode || encoder)(build(...args).reverse()) @@ -249,7 +249,7 @@ export let treePath = * @signature prop -> treePathBuilderFunction */ export let propTreePath = (prop) => - treePath(_.flow(treeValues, _.map(prop)), slashEncoder) + treePath(_.flow(treeValues, _.map(prop)), __internalSlashEncoder) /** * Creates a flat object with a property for each node, using `buildPath` to determine the keys. `buildPath` takes the same arguments as a tree walking iteratee. It will default to a dot tree path. diff --git a/test/tree.spec.js b/test/tree.spec.js index 8f3660c9..74b60ae8 100644 --- a/test/tree.spec.js +++ b/test/tree.spec.js @@ -22,6 +22,12 @@ describe('Tree Functions', () => { } expect(F.traverse(x)).to.deep.equal(x) }) + describe('treePath', () => { + it('should not remove zeroes', () => { + let path = F.treePath()(null, 'first', [], ['second', 0, 'third']) + expect(path).to.equal('third.0.second.first') + }) + }) describe('walk', () => { let x = { a: 1, From 7ba3a58679ec01696def0ef0196a0917efd3c9d7 Mon Sep 17 00:00:00 2001 From: Alejandro Hernandez Date: Wed, 7 Feb 2024 16:07:21 -0500 Subject: [PATCH 2/2] Use isBlank --- src/array.js | 18 ------------------ src/tree.js | 11 +++++++---- test/tree.spec.js | 6 ------ 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/array.js b/src/array.js index 7a6aaa9b..e455184a 100644 --- a/src/array.js +++ b/src/array.js @@ -173,24 +173,6 @@ export let dotEncoder = encoder('.') */ export let slashEncoder = encoder('/') -// The name "pack" is tentative. It's intended to behave as `_.compact` without -// removing the number 0. -let pack = _.remove( - (x) => x === undefined || x === null || x === false || x === '' -) - -// Internal use until we release next major -let __internalEncoder = (separator) => ({ - encode: _.flow(pack, _.join(separator)), - decode: _.split(separator), -}) - -// Internal use until we release next major -export let __internalDotEncoder = __internalEncoder('.') - -// Internal use until we release next major -export let __internalSlashEncoder = __internalEncoder('/') - /** * Takes a predicate function and an array, and returns an array of arrays where each element has one or more elements of the original array. Similar to Haskell's [groupBy](http://zvon.org/other/haskell/Outputlist/groupBy_f.html). diff --git a/src/tree.js b/src/tree.js index 2498118d..48b3c1b8 100644 --- a/src/tree.js +++ b/src/tree.js @@ -7,7 +7,8 @@ import _ from 'lodash/fp' import { findIndexed } from './conversion' -import { pushOn, __internalDotEncoder, __internalSlashEncoder } from './array' +import { pushOn } from './array' +import { isBlank } from './lang' /** * A default check if something can be traversed - currently it is arrays and plain objects. @@ -233,23 +234,25 @@ export let treeKeys = (x, i, xs, is) => [i, ...is] */ export let treeValues = (x, i, xs) => [x, ...xs] +let pathEncoder = (separator) => _.flow(_.remove(isBlank), _.join(separator)) + /** * Creates a path builder for use in `flattenTree`. By default, the builder will look use child indexes and a dotEncoder. Encoder can be an encoding function or a futil `encoder` (an object with encode and decode functions) * * @signature (build, encoder) -> treePathBuilderFunction */ export let treePath = - (build = treeKeys, encoder = __internalDotEncoder) => + (build = treeKeys, encoder = pathEncoder('.')) => (...args) => (encoder.encode || encoder)(build(...args).reverse()) /** - * Creates a path builder for use in `flattenTree`, using a slashEncoder and using the specified prop function as an iteratee on each node to determine the keys. + * Creates a path builder for use in `flattenTree`, using a pathEncoder and using the specified prop function as an iteratee on each node to determine the keys. * * @signature prop -> treePathBuilderFunction */ export let propTreePath = (prop) => - treePath(_.flow(treeValues, _.map(prop)), __internalSlashEncoder) + treePath(_.flow(treeValues, _.map(prop)), pathEncoder('/')) /** * Creates a flat object with a property for each node, using `buildPath` to determine the keys. `buildPath` takes the same arguments as a tree walking iteratee. It will default to a dot tree path. diff --git a/test/tree.spec.js b/test/tree.spec.js index 74b60ae8..8f3660c9 100644 --- a/test/tree.spec.js +++ b/test/tree.spec.js @@ -22,12 +22,6 @@ describe('Tree Functions', () => { } expect(F.traverse(x)).to.deep.equal(x) }) - describe('treePath', () => { - it('should not remove zeroes', () => { - let path = F.treePath()(null, 'first', [], ['second', 0, 'third']) - expect(path).to.equal('third.0.second.first') - }) - }) describe('walk', () => { let x = { a: 1,