diff --git a/CHANGELOG b/CHANGELOG index 94414c9..667a4dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +# 1.8.1 +- feat: extended pick functionality by using multiple path parameters and nested pick + # 1.8.0 - feat: max - feat: min diff --git a/package.json b/package.json index 6069abc..a7ca138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bitfinexcom/lib-js-util-base", - "version": "1.8.0", + "version": "1.8.1", "description": "general utils", "main": "index.js", "scripts": { diff --git a/src/get.js b/src/get.js index 7fc8ffe..83b2917 100644 --- a/src/get.js +++ b/src/get.js @@ -1,14 +1,6 @@ 'use strict' -/** - * - * @param {string} path - * @returns {Array} - */ -const pathToArray = (path) => { - if (Array.isArray(path)) return path - return path.split(/\[|\]\.|\]\[|\.|\]/gm).filter((key) => key !== '') -} +const pathToArray = require('./util/pathToArray') /** * diff --git a/src/pick.js b/src/pick.js index b2a2a3d..ab0a636 100644 --- a/src/pick.js +++ b/src/pick.js @@ -1,14 +1,65 @@ 'use strict' -const pick = (obj, keys) => { +const get = require('./get') +const pathToArray = require('./util/pathToArray') + +/** + * + * @param {any} nested + * @param {Array} path + * @param {any} value + * @returns + */ +const _setObjByPath = (nested, path, value) => { + if (path.length === 1) { + return Object.assign(nested, { [path[0]]: value }) + } + + const currentProp = path.shift() + if (nested[currentProp] === undefined) { + nested[currentProp] = {} + } + + return _setObjByPath(nested[currentProp], path, value) +} + +/** + * + * @param {object} src + * @param {object} dest + * @param {string | Array} path + * @returns + */ +const _pickByPath = (src, dest, path) => { + const pathArr = pathToArray(path) + + const value = get(src, [...pathArr]) + if (value !== undefined) { + _setObjByPath(dest, pathArr, value) + } + return dest +} + +/** + * + * @param {any} obj + * @param {...(string | Array>)} pathes + * @returns + */ +const pick = (obj, ...pathes) => { if (obj === null) { return {} } const newObj = {} - for (const key in obj) { - if (keys.includes(key)) { - newObj[key] = obj[key] + for (const path of pathes) { + if (typeof path === 'string') { + _pickByPath(obj, newObj, path) + } + if (Array.isArray(path)) { + for (const chunk of path) { + _pickByPath(obj, newObj, chunk) + } } } diff --git a/src/util/pathToArray.js b/src/util/pathToArray.js new file mode 100644 index 0000000..47e0d68 --- /dev/null +++ b/src/util/pathToArray.js @@ -0,0 +1,13 @@ +'use strict' + +/** + * + * @param {string} path + * @returns {Array} + */ +const pathToArray = (path) => { + if (Array.isArray(path)) return path + return path.split(/\[|\]\.|\]\[|\.|\]/gm).filter((key) => key !== '') +} + +module.exports = pathToArray diff --git a/test/pick.js b/test/pick.js index 87d6058..48f8be6 100644 --- a/test/pick.js +++ b/test/pick.js @@ -29,4 +29,49 @@ describe('pick', () => { it('should handle keys that are not strings', () => { assert.deepStrictEqual(pick({ 1: 'one', 2: 'two' }, ['1', '2']), { 1: 'one', 2: 'two' }) }) + + it('should pick properties by few path parameters', () => { + const src = { a: 1, b: 2, c: 3, d: 4 } + const expected = { a: 1, b: 2, d: 4 } + assert.deepStrictEqual(pick(src, ['a', 'b'], 'd'), expected) + }) + + it('should pick nested properties by string or array path', () => { + const src = { + a: 1, + b: { + c: { d: 5, g: 2, h: 7 }, + f: 6 + }, + e: 4 + } + const expected = { + a: 1, + b: { + c: { d: 5, h: 7 } + } + } + + assert.deepStrictEqual( + pick( + src, + ['a', 'b.c.d', 'b.c.h'], + 'd' + ), + expected + ) + assert.deepStrictEqual( + pick( + src, + ['a', ['b', 'c', 'd'], ['b', 'c', 'h']], + 'd' + ), + expected + ) + }) + + it('should return empty object if path is not specified', () => { + const src = { a: 1, b: { c: { d: 5, g: 2, h: 7 }, f: 6 }, e: 4 } + assert.deepStrictEqual(pick(src, ''), {}) + }) })