diff --git a/src/core.js b/src/core.js index 6cb4af3..ecf7ebe 100644 --- a/src/core.js +++ b/src/core.js @@ -1,220 +1,50 @@ const _ = require("./impl"); +const navs = require("./navs"); const NONE = _.NONE; -module.exports.NONE = NONE; +const COMPILED = Symbol("COMPILED"); -const navigator = m => { - const fn = next => operation => m[operation](next(operation)); - fn.isNavigator = true; - return fn; -}; - -module.exports.ALL = navigator({ - select: next => struct => _.flatMap(next, struct), - transform: next => struct => _.map(next, struct) -}); - -module.exports.MAP_VALS = navigator({ - select: next => struct => _.flatMap(next, _.values(struct)), - transform: next => struct => _.mapValues(next, struct) -}); - -module.exports.MAP_KEYS = navigator({ - select: next => struct => _.flatMap(next, _.keys(struct)), - transform: next => struct => _.mapKeys(next, struct) -}); - -module.exports.MAP_ENTRIES = navigator({ - select: next => struct => _.flatMap(next, _.entries(struct)), - transform: next => struct => _.mapEntries(next, struct) -}); - -module.exports.FIRST = navigator({ - select: next => struct => (_.isEmpty(struct) ? [] : next(struct[0])), - transform: next => struct => - _.isEmpty(struct) ? struct : _.updateArray(0, next, struct) -}); - -module.exports.LAST = navigator({ - select: next => struct => - _.isEmpty(struct) ? [] : next(struct[struct.length - 1]), - transform: next => struct => - _.isEmpty(struct) ? struct : _.updateArray(struct.length - 1, next, struct) -}); - -module.exports.BEGINNING = navigator({ - select: next => struct => [], - transform: next => struct => { - const result = next([]); - return _.isArray(result) - ? _.concat(result, struct) - : _.cons(result, struct); - } -}); - -module.exports.END = navigator({ - select: next => struct => [], - transform: next => struct => { - const result = next([]); - return _.isArray(result) - ? _.concat(struct, result) - : _.conj(struct, result); - } -}); - -module.exports.BEFORE_ELEM = navigator({ - select: next => struct => NONE, - transform: next => struct => { - const result = next(NONE); - return result === NONE ? struct : _.cons(result, struct); - } -}); - -module.exports.AFTER_ELEM = navigator({ - select: next => struct => NONE, - transform: next => struct => { - const result = next(NONE); - return result === NONE ? struct : _.conj(struct, result); - } -}); - -module.exports.key = key => - navigator({ - select: next => struct => next(struct[key]), - transform: next => struct => { - const result = next(struct[key]); - return result === NONE ? _.omit(key, struct) : _.set(key, result, struct); - } - }); - -module.exports.nth = index => - navigator({ - select: next => struct => next(struct[index]), - transform: next => struct => _.updateArray(index, next, struct) - }); - -module.exports.pred = pred => - navigator({ - select: next => struct => (pred(struct) ? next(struct) : []), - transform: next => struct => (pred(struct) ? next(struct) : struct) - }); - -module.exports.parser = (parse, unparse) => - navigator({ - select: next => struct => next(parse(struct)), - transform: next => struct => unparse(next(parse(struct))) - }); - -module.exports.submap = keys => - navigator({ - select: next => struct => next(_.pick(keys, struct)), - transform: next => struct => _.merge(struct, next(_.pick(keys, struct))) - }); +const isCompiled = obj => typeof obj === "function" && obj[COMPILED]; -module.exports.view = fn => - navigator({ - select: next => struct => next(fn(struct)), - transform: next => struct => next(fn(struct)) - }); - -module.exports.filterer = path => { - const compiledPath = compile(path); - return navigator({ - select: next => struct => - next( - _.reduce( - (acc, v) => { - const result = module.exports.compiledSelect(compiledPath, v); - return result.length ? _.conj(acc, v) : acc; - }, - [], - struct - ) - ), - transform: next => struct => { - const mapping = {}; - const filtered = []; - for (let [i, j] = [0, 0]; i < struct.length; i++) { - const selected = module.exports.compiledSelect(compiledPath, struct[i]); - if (selected.length) { - mapping[i] = j; - j++; - filtered.push(...selected); - } - } - - const transformed = next(filtered); - const result = []; - for (let i = 0; i < struct.length; i++) { - const mappedIdx = mapping[i]; - - if (mappedIdx !== undefined) { - if (transformed[mappedIdx] !== undefined) { - result.push(transformed[mappedIdx]); - } - } else { - result.push(struct[i]); - } - } - - return result; - } - }); -}; - -const resolveNavigator = nav => { - const type = typeof nav; - if (type === "function" && nav.isNavigator) { - return nav; - } - - switch (type) { - case "string": - return module.exports.key(nav); - case "number": - return module.exports.nth(nav); - case "function": - return module.exports.pred(nav); +const compile = path => { + if (isCompiled(path)) { + return path; } -}; -const compile = path => { + path = Array.isArray(path) ? path : [path]; let defer; - const compiled = _.reduceRight( - (acc, nav) => resolveNavigator(nav)(acc), - op => v => defer(v), - Array.isArray(path) ? path : [path] - ); - - return (operation, lastFn, struct) => { - defer = lastFn; - return compiled(operation)(struct); + const reducer = (acc, nav) => { + return isCompiled(nav) + ? op => struct => nav(op, acc(op), struct) + : navs.resolveNavigator(nav)(acc); }; -}; -module.exports.compile = compile; + const compiled = _.reduceRight(reducer, op => defer, path); -module.exports.select = (path, struct) => - compile(path)("select", v => [v], struct); + const fn = (operation, leafFn, struct) => { + defer = leafFn; + return compiled(operation)(struct); + }; -module.exports.compiledSelect = (compiledPath, struct) => - compiledPath("select", v => [v], struct); + fn[COMPILED] = true; + return fn; +}; -module.exports.selectOne = (path, struct) => - compile(path)("select", v => v, struct); +const select = (path, struct) => compile(path)("select", v => [v], struct); -module.exports.compiledSelectOne = (compiledPath, struct) => - compiledPath("select", v => v, struct); +const selectOne = (path, struct) => compile(path)("select", v => v, struct); -module.exports.transform = (path, update, struct) => +const transform = (path, update, struct) => compile(path)("transform", update, struct); -module.exports.compiledTransform = (compiledPath, update, struct) => - compiledPath("transform", update, struct); - -module.exports.setval = (path, value, struct) => +const setval = (path, value, struct) => compile(path)("transform", () => value, struct); -module.exports.compiledSetval = (compiledPath, value, struct) => - compiledPath("transform", () => value, struct); +module.exports = { + compile, + select, + selectOne, + transform, + setval +}; diff --git a/src/impl.js b/src/impl.js index 2cf0969..dd67aaf 100644 --- a/src/impl.js +++ b/src/impl.js @@ -1,6 +1,9 @@ const NONE = Symbol("NONE"); module.exports.NONE = NONE; +/** + * Map over an array or string. NONE values are removed. + */ module.exports.map = (fn, struct) => { const acc = []; for (let i = 0; i < struct.length; i++) { @@ -9,9 +12,12 @@ module.exports.map = (fn, struct) => { acc.push(result); } } - return acc; + return typeof struct === "string" ? acc.join("") : acc; }; +/** + * Map over an array and concat the results. NONE values are removed. + */ module.exports.flatMap = (fn, struct) => { const acc = []; for (let i = 0; i < struct.length; i++) { @@ -27,38 +33,68 @@ module.exports.flatMap = (fn, struct) => { return acc; }; -module.exports.concat = (a, b) => a.concat(b); - -module.exports.reduceRight = (fn, initial, struct) => { +/** + * Reduce array from left to right. + */ +module.exports.reduce = (fn, initial, struct) => { let acc = initial; - for (let i = struct.length - 1; i >= 0; i--) { + for (let i = 0; i < struct.length; i++) { acc = fn(acc, struct[i]); } return acc; }; -module.exports.reduce = (fn, initial, struct) => { +/** + * Reduce array from right to left. + */ +module.exports.reduceRight = (fn, initial, struct) => { let acc = initial; - for (let i = 0; i < struct.length; i++) { + for (let i = struct.length - 1; i >= 0; i--) { acc = fn(acc, struct[i]); } return acc; }; +/** + * concat 2 arrays. + */ +module.exports.concat = (a, b) => a.concat(b); + +/** + * Add a value to the beginning of an array. + */ module.exports.cons = (a, b) => [a].concat(b); +/** + * Add a value to the end of an array. + */ module.exports.conj = (a, b) => { const r = a.slice(); r.push(b); return r; }; +/** + * Remove and return the first element from an array. Transforms undefined + * values to NONE. This operation mutates the array. + */ +module.exports.shift = struct => { + const v = struct.shift(); + return v === undefined ? NONE : v; +}; + +/** + * Insert element in an array at index. + */ module.exports.insertArray = (idx, value, struct) => { const r = struct.slice(0, idx); r.push(value, ...struct.slice(idx)); return r; }; +/** + * Update element in an array at index. NONE values are removed. + */ module.exports.updateArray = (idx, fn, struct) => { const result = fn(struct[idx]); if (result === NONE) { @@ -72,10 +108,27 @@ module.exports.updateArray = (idx, fn, struct) => { } }; +/** + * Return whether a value is an array. + */ module.exports.isArray = Array.isArray; +/** + * Return whether an array is empty. + */ module.exports.isEmpty = struct => !struct || struct.length === 0; +/** + * Return whether a value is an object. + */ +module.exports.isObject = struct => + typeof struct === "object" && + struct !== null && + struct.constructor === Object; + +/** + * Omit collection of keys from an object. + */ module.exports.omit = (keys, struct) => { const acc = {}; const Objkeys = Object.keys(struct); @@ -88,6 +141,9 @@ module.exports.omit = (keys, struct) => { return acc; }; +/** + * Pick collection of keys from an object. + */ module.exports.pick = (keys, struct) => { const acc = {}; for (let i = 0; i < keys.length; i++) { @@ -97,6 +153,9 @@ module.exports.pick = (keys, struct) => { return acc; }; +/** + * Merge 2 objects. + */ module.exports.merge = (a, b) => { const acc = {}; const keysA = Object.keys(a); @@ -114,6 +173,9 @@ module.exports.merge = (a, b) => { return acc; }; +/** + * Map over an object's values. NONE values are removed. + */ module.exports.mapValues = (fn, struct) => { const acc = {}; const keys = Object.keys(struct); @@ -128,6 +190,9 @@ module.exports.mapValues = (fn, struct) => { return acc; }; +/** + * Map over an object's keys. NONE values are removed. + */ module.exports.mapKeys = (fn, struct) => { const acc = {}; const keys = Object.keys(struct); @@ -142,6 +207,9 @@ module.exports.mapKeys = (fn, struct) => { return acc; }; +/** + * Map over an object's entries. NONE values are removed. + */ module.exports.mapEntries = (fn, struct) => { const acc = {}; const keys = Object.keys(struct); @@ -156,11 +224,25 @@ module.exports.mapEntries = (fn, struct) => { return acc; }; +module.exports.getIn = (keys, struct) => { + let acc = struct; + for (let i = 0; i < keys.length; i++) { + acc = acc[keys[i]]; + } + return acc; +}; + module.exports.set = (key, value, struct) => module.exports.merge(struct, { [key]: value }); +/** + * Return array of object's keys. + */ module.exports.keys = Object.keys; +/** + * Return array of object's values. + */ module.exports.values = obj => { const acc = []; const keys = Object.keys(obj); @@ -170,6 +252,9 @@ module.exports.values = obj => { return acc; }; +/** + * Return array of object's entries. + */ module.exports.entries = obj => { const acc = []; const keys = Object.keys(obj); diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..c333556 --- /dev/null +++ b/src/index.js @@ -0,0 +1,5 @@ +const navs = require("./navs"); +const core = require("./core"); +const impl = require("./impl"); + +module.exports = Object.assign({}, core, navs, { NONE: impl.NONE }); diff --git a/src/navs.js b/src/navs.js new file mode 100644 index 0000000..33ce9f5 --- /dev/null +++ b/src/navs.js @@ -0,0 +1,285 @@ +const _ = require("./impl"); +const core = require("./core"); + +const NONE = _.NONE; +const NAVIGATOR = Symbol("NAVIGATOR"); + +/** + * Create a navigator instance. A navigator is defined as an object with select + * and transform fields. + */ +const navigator = definition => { + const fn = next => operation => { + return definition[operation](next(operation)); + }; + fn[NAVIGATOR] = true; + return fn; +}; + +/** + * Return whether an object is a navigator. + */ +const isNavigator = nav => typeof nav === "function" && nav[NAVIGATOR]; + +const resolveNavigator = nav => { + if (isNavigator(nav)) { + return nav; + } + switch (typeof nav) { + case "string": + return keypath(nav); + case "number": + return nthpath(nav); + case "function": + return pred(nav); + } +}; + +/** + * Navigate to every element in a collection. Can transform to NONE to remove + * elements. Can navigate to elements in an array, entries in an object, or + * substrings in a string. + */ +const ALL = navigator({ + select: next => struct => + _.flatMap(next, _.isObject(struct) ? _.entries(struct) : struct), + transform: next => struct => { + console.log("ALL", struct); + return _.isObject(struct) + ? _.mapEntries(next, struct) + : _.map(next, struct); + } +}); + +/** + * Navigate to every value in a object. Can transform to NONE to remove object + * entries. + */ +const MAP_VALS = navigator({ + select: next => struct => + _.flatMap(next, _.isObject(struct) ? _.values(struct) : struct), + transform: next => struct => + _.isObject(struct) ? _.mapValues(next, struct) : _.map(next, struct) +}); + +/** + * Navigate to every key in a object. Can transform to NONE to remove object + * entries. + */ +const MAP_KEYS = navigator({ + select: next => struct => _.flatMap(next, _.keys(struct)), + transform: next => struct => _.mapKeys(next, struct) +}); + +/** + * Navigate to every key value pair in a object. Can transform to NONE to + * remove object entries. + */ +const MAP_ENTRIES = navigator({ + select: next => struct => _.flatMap(next, _.entries(struct)), + transform: next => struct => _.mapEntries(next, struct) +}); + +/** + * Navigate to the first element of a array. If the array is empty. navigation + * stops. Can tranform to NONE to remove elements. + */ +const FIRST = navigator({ + select: next => struct => (_.isEmpty(struct) ? [] : next(struct[0])), + transform: next => struct => + _.isEmpty(struct) ? struct : _.updateArray(0, next, struct) +}); + +/** + * Navigate to the last element of a array. If the array is empty. navigation + * stops. Can tranform to NONE to remove elements. + */ +const LAST = navigator({ + select: next => struct => { + return _.isEmpty(struct) ? [] : next(struct[struct.length - 1]); + }, + + transform: next => struct => + _.isEmpty(struct) ? struct : _.updateArray(struct.length - 1, next, struct) +}); + +const TAIL = navigator({ + select: next => struct => next(struct.slice(1)), + transform: next => struct => + _.concat(struct.slice(0, 1), next(struct.slice(1))) +}); + +/** + * Navigate to the empty array before the beginning of a array. + */ +const BEGINNING = navigator({ + select: next => struct => [], + transform: next => struct => { + const result = next([]); + return _.isArray(result) + ? _.concat(result, struct) + : _.cons(result, struct); + } +}); + +/** + * Navigate to the empty array after the end of a array. + */ +const END = navigator({ + select: next => struct => [], + transform: next => struct => { + const result = next([]); + return _.isArray(result) + ? _.concat(struct, result) + : _.conj(struct, result); + } +}); + +/** + * Navigate to the 'void' element before the array. + */ +const BEFORE_ELEM = navigator({ + select: next => struct => NONE, + transform: next => struct => { + const result = next(NONE); + return result === NONE ? struct : _.cons(result, struct); + } +}); + +/** + * Navigate to the 'void' element after the array. + */ +const AFTER_ELEM = navigator({ + select: next => struct => NONE, + transform: next => struct => { + const result = next(NONE); + return result === NONE ? struct : _.conj(struct, result); + } +}); + +const keypath = key => + navigator({ + select: next => struct => next(struct[key]), + transform: next => struct => { + const result = next(struct[key]); + return result === NONE ? _.omit(key, struct) : _.set(key, result, struct); + } + }); + +const nthpath = idx => + navigator({ + select: next => struct => next(struct[idx]), + transform: next => struct => _.updateArray(idx, next, struct) + }); + +const pred = pred => + navigator({ + select: next => struct => (pred(struct) ? next(struct) : []), + transform: next => struct => (pred(struct) ? next(struct) : struct) + }); + +const parser = (parse, unparse) => + navigator({ + select: next => struct => next(parse(struct)), + transform: next => struct => unparse(next(parse(struct))) + }); + +const submap = keys => + navigator({ + select: next => struct => next(_.pick(keys, struct)), + transform: next => struct => _.merge(struct, next(_.pick(keys, struct))) + }); + +const view = fn => + navigator({ + select: next => struct => next(fn(struct)), + transform: next => struct => next(fn(struct)) + }); + +const isSelected = path => { + path = core.compile(path); + return navigator({ + select: next => struct => + core.select(path, struct).length ? next(struct) : [], + transform: next => struct => + core.select(path, struct).length ? next(struct) : struct + }); +}; + +const subselect = path => { + path = core.compile(path); + return navigator({ + select: next => struct => next(core.select(path, struct)), + transform: next => struct => { + const transformed = next(core.select(path, struct)); + return core.transform(path, () => _.shift(transformed), struct); + } + }); +}; + +const filterer = path => { + path = core.compile(path); + return navigator({ + select: next => struct => + next(core.select(subselect([ALL, isSelected(path)]), struct)), + transform: next => struct => + core.transform(subselect([ALL, isSelected(path)]), next, struct) + }); +}; + +const branch = (...paths) => { + // paths = _.map(core.compile, paths); + return navigator({ + select: next => struct => + next(_.flatMap(path => core.select(path, struct), paths)), + transform: next => struct => { + return _.reduce( + (acc, path) => core.transform(path, v => next([v]), acc), + struct, + paths + ); + } + }); +}; + +const regex = pattern => + navigator({ + select: next => struct => { + const match = struct.match(pattern); + return match ? _.flatMap(next, match) : []; + }, + transform: next => struct => { + console.log("regex", struct); + const replaced = struct.replace(pattern, next); + console.log("result", replaced); + return replaced; + } + }); + +module.exports = { + navigator, + isNavigator, + resolveNavigator, + ALL, + MAP_VALS, + MAP_KEYS, + MAP_ENTRIES, + FIRST, + LAST, + TAIL, + BEGINNING, + END, + BEFORE_ELEM, + AFTER_ELEM, + keypath, + nthpath, + pred, + parser, + submap, + view, + filterer, + isSelected, + subselect, + branch, + regex +}; diff --git a/tests/select.test.js b/tests/select.test.js index 7c46b72..551308f 100644 --- a/tests/select.test.js +++ b/tests/select.test.js @@ -1,4 +1,4 @@ -const s = require("../src/core"); +const s = require("../src"); const select = (path, struct, expected) => expect(s.select(path, struct)).toEqual(expected); @@ -17,6 +17,12 @@ describe("select", () => { select(s.ALL, [1, 2], [1, 2]); select(s.ALL, [[1, 2]], [[1, 2]]); select([s.ALL, s.ALL], [[1, 2]], [1, 2]); + + select(s.ALL, {}, []); + select(s.ALL, { a: 1 }, [["a", 1]]); + + select(s.ALL, "", []); + select(s.ALL, "abc", ["a", "b", "c"]); }); test("MAP_VALS", () => { @@ -103,12 +109,18 @@ describe("select", () => { }); test("filterer", () => { - select(s.filterer(even), [1, 2, 3, 4], [[2, 4]]); - select([s.filterer(even), s.ALL], [1, 2, 3, 4], [2, 4]); + select(s.filterer(even), [1, 2, 3, 4], [[[2, 4]]]); + select([s.filterer(even), s.ALL], [1, 2, 3, 4], [[2, 4]]); }); test("complex", () => { select([s.ALL, s.FIRST], [[1, 2], [1, 2]], [1, 1]); + select([s.ALL, s.compile([s.FIRST])], [[1, 2], [1, 2]], [1, 1]); + select( + [s.ALL, s.compile(s.FIRST), s.LAST], + [[[1, 2], ["a", "b"]], [[3, 4], ["c", "d"]]], + [2, 4] + ); select([s.ALL, "a"], [{ a: 1 }, { a: 1 }], [1, 1]); select([s.ALL, "a", v => v > 1], [{ a: 1 }, { a: 2 }], [2]); }); @@ -119,19 +131,11 @@ describe("select variants", () => { const path = [s.ALL, "a", v => v > 1]; const compiledPath = s.compile(path); - test("select", () => { - expect(s.select(path, data)).toEqual([2]); - }); - test("compiled select", () => { - expect(s.compiledSelect(compiledPath, data)).toEqual([2]); + expect(s.select(compiledPath, data)).toEqual([2]); }); test("select-one", () => { expect(s.selectOne(path, data)).toEqual([2]); }); - - test("compiled select-one", () => { - expect(s.compiledSelectOne(compiledPath, data)).toEqual([2]); - }); }); diff --git a/tests/transform.test.js b/tests/transform.test.js index 707a267..8f279c3 100644 --- a/tests/transform.test.js +++ b/tests/transform.test.js @@ -1,4 +1,4 @@ -const s = require("../src/core"); +const s = require("../src"); const transform = (path, fn, struct, expected) => expect(s.transform(path, fn, struct)).toEqual(expected); @@ -14,33 +14,62 @@ describe("transform", () => { test("Without navigators", () => { transform([], identity, 1, 1); transform([], identity, [], []); - }); - - test("ALL", () => { - transform(s.ALL, identity, [1], [1]); - transform(s.ALL, inc, [1, 2], [2, 3]); - transform([s.ALL, s.ALL], inc, [[1, 2], [3, 4]], [[2, 3], [4, 5]]); - transform(s.ALL, constant(s.NONE), [1, 2, 3], []); - }); - - test("MAP_VALS", () => { - transform(s.MAP_VALS, inc, {}, {}); - transform(s.MAP_VALS, inc, { a: 1 }, { a: 2 }); - }); - - test("MAP_KEYS", () => { - transform(s.MAP_KEYS, inc, {}, {}); - transform(s.MAP_KEYS, v => v + "b", { a: "a" }, { ab: "a" }); - }); - - test("MAP_ENTRIES", () => { - transform(s.MAP_ENTRIES, identity, {}, {}); - transform( - s.MAP_ENTRIES, - ([k, v]) => [k + "b", v + "b"], - { a: "a" }, - { ab: "ab" } - ); + transform([], identity, {}, {}); + transform([], identity, undefined, undefined); + }); + + describe("ALL", () => { + describe("array", () => { + test("to value", () => transform(s.ALL, inc, [1, 2], [2, 3])); + test("to NONE", () => transform(s.ALL, constant(s.NONE), [1, 2, 3], [])); + }); + describe("object", () => { + test("to value", () => { + const update = v => [v[0] + "x", inc(v[1])]; + transform(s.ALL, update, { a: 1, b: 2 }, { ax: 2, bx: 3 }); + }); + test("to NONE", () => + transform(s.ALL, constant(s.NONE), { a: 1, b: 2 }, {})); + }); + describe("string", () => { + test("to value", () => transform(s.ALL, v => v + "x", "abc", "axbxcx")); + test("to NONE", () => transform(s.ALL, constant(s.NONE), "abc", "")); + }); + test("calls next nav", () => + transform([s.ALL, s.ALL], inc, [[1, 2], [3, 4]], [[2, 3], [4, 5]])); + }); + + describe("MAP_VALS", () => { + describe("array", () => { + test("to value", () => transform(s.MAP_VALS, inc, [1, 2], [2, 3])); + test("to NONE", () => + transform(s.MAP_VALS, constant(s.NONE), [1, 2], [])); + }); + describe("object", () => { + test("to value", () => + transform(s.MAP_VALS, inc, { a: 1, b: 2 }, { a: 2, b: 3 })); + test("to NONE", () => + transform(s.MAP_VALS, constant(s.NONE), { a: 1, b: 2 }, {})); + }); + test("calls next nav", () => + transform( + [s.MAP_VALS, s.MAP_VALS], + inc, + [[1, 2], [3, 4]], + [[2, 3], [4, 5]] + )); + }); + + describe("MAP_KEYS", () => { + describe("object", () => { + test("to value", () => + transform(s.MAP_KEYS, v => v + "b", { a: "a" }, { ab: "a" })); + test("to NONE", () => + transform(s.MAP_KEYS, constant(s.NONE), { a: 1, b: 2 }, {})); + }); + + test("calls next nav", () => + transform([s.MAP_KEYS, s.ALL], v => v + "x", { ab: "a" }, { axbx: "a" })); }); test("FIRST", () => { @@ -116,7 +145,6 @@ describe("transform", () => { }); test("filterer", () => { - expect(reverse([1, 2, 3])).toEqual([3, 2, 1]); transform(s.filterer(even), reverse, [1, 2, 3, 4, 5], [1, 4, 3, 2, 5]); transform( [s.filterer(even), s.ALL], @@ -132,10 +160,37 @@ describe("transform", () => { ); }); + test("subselect", () => { + const r = s.select( + [s.subselect([s.ALL, "a", even])], + [{ a: 1 }, { a: 2 }, { a: 4 }] + ); + + expect(r).toEqual([[2, 4]]); + + const r2 = s.transform([s.subselect([s.ALL, "a", even])], reverse, [ + { a: 1 }, + { a: 2 }, + { a: 4 } + ]); + + expect(r2).toEqual([{ a: 1 }, { a: 4 }, { a: 2 }]); + }); + test("complex", () => { transform([s.ALL, even], inc, [1, 2, 3], [1, 3, 3]); transform([s.ALL, even], constant(s.NONE), [1, 2, 3], [1, 3]); }); + + test("branch", () => { + transform([s.branch(0, 1)], inc, [1, 2, 3], [2, 3, 3]); + transform( + s.branch("a", "b"), + inc, + { a: 1, b: 2, c: 3 }, + { a: 2, b: 3, c: 3 } + ); + }); }); describe("transform variants", () => { @@ -146,17 +201,11 @@ describe("transform variants", () => { test("transform", () => { expect(s.transform(path, inc, data)).toEqual(expected); - }); - - test("compiled transform", () => { - expect(s.compiledTransform(compiledPath, inc, data)).toEqual(expected); + expect(s.transform(compiledPath, inc, data)).toEqual(expected); }); test("setval", () => { expect(s.setval(path, 3, data)).toEqual(expected); - }); - - test("compiled setval", () => { - expect(s.compiledSetval(compiledPath, 3, data)).toEqual(expected); + expect(s.setval(compiledPath, 3, data)).toEqual(expected); }); });