diff --git a/src/utils/index.js b/src/utils/index.js index c27687fd..2c11c3d2 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -14,6 +14,7 @@ import { convertKeysToCamelCaseForObject, flattenObject, getOneMatchFromObject, + mergeTerminalNodes, renameKeysForObject, safeMakeObject, sortKeysDeepForObject, @@ -71,4 +72,5 @@ export { replaceUnit, mapTree, getSchemaWithDependencies, + mergeTerminalNodes, }; diff --git a/src/utils/object.js b/src/utils/object.js index 5cf2bcde..ab14a3a1 100644 --- a/src/utils/object.js +++ b/src/utils/object.js @@ -176,3 +176,41 @@ export function sortKeysDeepForObject(obj) { } return obj; } + +/** + * Merge terminal node values of an object tree. + * @param {Object} tree - Nested object + * @param {Boolean} unique - Whether merged list should consist of unique items + * @return {Array} - Merged list of values of terminal nodes. + * @example + * const tree = { + * level1: { + * level2a: { + * level3a: { + * key1: ['a', 'b', 'c'], + * }, + * level3b: { + * key2: ['d', 'e', 'f'], + * }, + * }, + * level2b: { + * level3c: { + * key3: ['g', 'h', 'i'], + * }, + * level3d: { + * key4: ['j', 'k', 'l'], + * }, + * }, + * }, + * }; + * mergeTerminalNodes(tree); // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'] + */ +export function mergeTerminalNodes(tree, unique = false) { + const terminalValues = lodash.values(tree).reduce((accumulator, value) => { + if (lodash.isPlainObject(value)) { + return accumulator.concat(mergeTerminalNodes(value)); + } + return accumulator.concat(value); + }, []); + return unique ? [...new Set(terminalValues)] : terminalValues; +} diff --git a/tests/object.tests.js b/tests/object.tests.js index 9a230144..ac372b68 100644 --- a/tests/object.tests.js +++ b/tests/object.tests.js @@ -1,6 +1,6 @@ import { expect } from "chai"; -import { flattenObject } from "../src/utils/object"; +import { flattenObject, mergeTerminalNodes } from "../src/utils/object"; describe("flattenObject", () => { it("serializes simple object", () => { @@ -46,3 +46,40 @@ describe("flattenObject", () => { }).to.throw(); }); }); + +describe("mergeTerminalNodes", () => { + it("merges terminal nodes containing an array of strings", () => { + const treeStrings = { + level1: { + level2a: { + level3a: { + key1: ["a", "b", "c"], + }, + level3b: { + key2: ["d", "e", "f"], + }, + }, + }, + }; + const merged = mergeTerminalNodes(treeStrings); + expect(merged).to.have.members(["a", "b", "c", "d", "e", "f"]); + }); + + it("merges terminal nodes containing an array of objects", () => { + const treeObjects = { + level1: { + level2a: { + level3a: { + key1: [{ path: "a" }, { path: "b" }, { path: "c" }], + }, + level3b: { + key2: [{ path: "d" }, { path: "e" }, { path: "f" }], + }, + }, + }, + }; + const merged = mergeTerminalNodes(treeObjects); + expect(merged).to.have.length(6); + expect(merged.map((o) => o.path)).to.have.members(["a", "b", "c", "d", "e", "f"]); + }); +});