package/package.json000644 0000002647 13276012113011572 0ustar00000000 000000 { "name": "copy-props", "version": "2.0.4", "description": "Copy properties deeply between two objects.", "main": "index.js", "files": [ "index.js" ], "scripts": { "lint": "eslint .", "test": "mocha", "coverage": "nyc --reporter=lcov --reporter=text-summary npm test", "coveralls": "nyc --reporter=text-lcov npm test | coveralls", "web:build": "browserify index.js --standalone copyProps -o web/copy-props.js && cd web && uglifyjs copy-props.js --compress --mangle -o copy-props.min.js --source-map url=copy-props.min.js.map", "chrome:install": "npm i --no-save mocha-chrome", "chrome:test": "mocha-chrome test/web/browser-test.html", "build": "npm run lint && npm run coverage && npm run web:build && node test/web/make.js" }, "repository": { "type": "git", "url": "git+https://github.com/sttk/copy-props.git" }, "keywords": [ "object", "property", "copy", "deep", "map", "convert" ], "author": "Takayuki Sato", "license": "MIT", "bugs": { "url": "https://github.com/sttk/copy-props/issues" }, "homepage": "https://github.com/sttk/copy-props#readme", "dependencies": { "each-props": "^1.3.0", "is-plain-object": "^2.0.1" }, "devDependencies": { "browserify": "^16.2.2", "chai": "^3.5.0", "coveralls": "^3.0.1", "eslint": "^4.19.1", "mocha": "^3.2.0", "nyc": "^11.7.2", "uglify-js": "^3.3.24" } } package/index.js000644 0000010535 13276012113010744 0ustar00000000 000000 'use strict'; var eachProps = require('each-props'); var isPlainObject = require('is-plain-object'); module.exports = function(src, dst, fromto, converter, reverse) { if (!isObject(src)) { src = {}; } if (!isObject(dst)) { dst = {}; } if (isPlainObject(fromto)) { fromto = onlyValueIsString(fromto); } else if (Array.isArray(fromto)) { fromto = arrayToObject(fromto); } else if (typeof fromto === 'boolean') { reverse = fromto; converter = noop; fromto = null; } else if (typeof fromto === 'function') { reverse = converter; converter = fromto; fromto = null; } else { fromto = null; } if (typeof converter !== 'function') { if (typeof converter === 'boolean') { reverse = converter; converter = noop; } else { converter = noop; } } if (typeof reverse !== 'boolean') { reverse = false; } if (reverse) { var tmp = src; src = dst; dst = tmp; if (fromto) { fromto = invert(fromto); } } var opts = { dest: dst, fromto: fromto, convert: converter, }; if (fromto) { eachProps(src, copyWithFromto, opts); setParentEmptyObject(dst, fromto); } else { eachProps(src, copyWithoutFromto, opts); } return dst; }; function copyWithFromto(value, keyChain, nodeInfo) { if (isPlainObject(value)) { return; } var dstKeyChains = nodeInfo.fromto[keyChain]; if (!dstKeyChains) { return; } delete nodeInfo.fromto[keyChain]; if (!Array.isArray(dstKeyChains)) { dstKeyChains = [dstKeyChains]; } var srcInfo = { keyChain: keyChain, value: value, key: nodeInfo.name, depth: nodeInfo.depth, parent: nodeInfo.parent, }; for (var i = 0, n = dstKeyChains.length; i < n; i++) { setDeep(nodeInfo.dest, dstKeyChains[i], function(parent, key, depth) { var dstInfo = { keyChain: dstKeyChains[i], value: parent[key], key: key, depth: depth, parent: parent, }; return nodeInfo.convert(srcInfo, dstInfo); }); } } function copyWithoutFromto(value, keyChain, nodeInfo) { if (isPlainObject(value)) { for (var k in value) { return; } setDeep(nodeInfo.dest, keyChain, newObject); return; } var srcInfo = { keyChain: keyChain, value: value, key: nodeInfo.name, depth: nodeInfo.depth, parent: nodeInfo.parent, }; setDeep(nodeInfo.dest, keyChain, function(parent, key, depth) { var dstInfo = { keyChain: keyChain, value: parent[key], key: key, depth: depth, parent: parent, }; return nodeInfo.convert(srcInfo, dstInfo); }); } function newObject() { return {}; } function noop(srcInfo) { return srcInfo.value; } function onlyValueIsString(obj) { var newObj = {}; for (var key in obj) { var val = obj[key]; if (typeof val === 'string') { newObj[key] = val; } } return newObj; } function arrayToObject(arr) { var obj = {}; for (var i = 0, n = arr.length; i < n; i++) { var elm = arr[i]; if (typeof elm === 'string') { obj[elm] = elm; } } return obj; } function invert(fromto) { var inv = {}; for (var key in fromto) { var val = fromto[key]; if (!inv[val]) { inv[val] = []; } inv[val].push(key); } return inv; } function setDeep(obj, keyChain, valueCreator) { _setDeep(obj, keyChain.split('.'), 1, valueCreator); } function _setDeep(obj, keyElems, depth, valueCreator) { var key = keyElems.shift(); if (!keyElems.length) { var value = valueCreator(obj, key, depth); if (value === undefined) { return; } if (isPlainObject(value)) { // value is always an empty object. if (isPlainObject(obj[key])) { return; } } obj[key] = value; return; } if (!isPlainObject(obj[key])) { obj[key] = {}; } _setDeep(obj[key], keyElems, depth + 1, valueCreator); } function setParentEmptyObject(obj, fromto) { for (var srcKeyChain in fromto) { var dstKeyChains = fromto[srcKeyChain]; if (!Array.isArray(dstKeyChains)) { dstKeyChains = [dstKeyChains]; } for (var i = 0, n = dstKeyChains.length; i < n; i++) { setDeep(obj, dstKeyChains[i], newUndefined); } } } function newUndefined() { return undefined; } function isObject(v) { return Object.prototype.toString.call(v) === '[object Object]'; } package/LICENSE000644 0000002056 13276012113010303 0ustar00000000 000000 MIT License Copyright (c) 2016 Takayuki Sato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package/README.md000644 0000015612 13276012113010557 0ustar00000000 000000 # [copy-props][repo-url] [![NPM][npm-img]][npm-url] [![MIT License][mit-img]][mit-url] [![Build Status][travis-img]][travis-url] [![Build Status][appveyor-img]][appveyor-url] [![Coverage Status][coverage-img]][coverage-url] Copy properties between two objects deeply. ## Install To install from npm: ```sh $ npm i copy-props --save ``` ## Load this module For Node.js: ```js const copyProps = require('copy-props'); ``` For Web browser: ```html ``` ## Usage Copy *src* to *dst* simply (and return *dst*) : ```js var src = { a: 1, b: { b1: 'bbb' }, c: 'ccc' }; var dst = { a: 2, b: { b1: 'xxx', b2: 'yyy' } }; copyProps(src, dst); // => { a: 1, b: { b1: 'bbb', b2: 'yyy' }, c: 'ccc' } ``` Copy *src* to *dst* with property mapping (and return *dst*) : ```js var src = { a: 1, b: { b1: 'bbb' }, c: 'ccc', d: 'ddd' }; var dst = { f: { a: 2, b1: 'xxx', b2: 'yyy' }, e: 'zzz' }; copyProps(src, dst, { a: 'f.a', 'b.b1': 'f.b1', 'b.b2': 'f.b2', 'c': 'f.c', }); // => { f: { a: 1, b1: 'bbb', b2: 'yyy', c: 'ccc' }, e: 'zzz' } ``` Copy *src* to *dst* with convert function (and return *dst*) : ```js var src = { a: 1, b: { b1: 'bbb' } }; var dst = { a: 0 }; copyProps(src, dst, function(srcInfo) { if (srcInfo.keyChain === 'a') { return srcInfo.value * 2; } if (srcInfo.keyChain === 'b.b1') { return srcInfo.value.toUpperCase(); } }); // => { a: 2, b: { b1: 'BBB' } } ``` Can use an array instead of a map as property mapping : ```js var src = { a: 1, b: { c: 'CCC' }, d: { e: 'EEE' } }; var dst = { a: 9, b: { c: 'xxx' }, d: { e: 'yyy' } }; var fromto = [ 'b.c', 'd.e' ]; copyProps(src, dst, fromto); // => { a: 9, b: { c: 'CCC' }, d: { e: 'EEE' } } ``` Can copy reversively (from *dst* to *src*) by reverse flag (and return *src*): ```js var src = { a: 1, b: { b1: 'bbb' }, c: 'ccc' }; var dst = { a: 2, b: { b1: 'xxx', b2: 'yyy' } }; copyProps(src, dst, true); // => { a: 2, b: { b1: 'xxx', b2: 'yyy' }, c: 'ccc' } ``` ```js var src = { a: 1, b: { b1: 'bbb' }, c: 'ccc', d: 'ddd' }; var dst = { f: { a: 2, b1: 'xxx', b2: 'yyy' }, e: 'zzz' }; copyProps(src, dst, { a: 'f.a', 'b.b2': 'f.b2', 'c': 'f.c', }, true); // => { a: 2, b: { b1: 'bbb', b2: 'yyy' }, c: 'ccc', d: 'ddd' } ``` If a value of source property is undefined (when not using converter), or a result of converter is undefined (when using converter), the value is not copied. ```js var src = { a: 'A', b: undefined, c: null, d: 1 }; var dst = { a: 'a', b: 'b', c: 'c' }; copyProps(src, dst, function(srcInfo) { if (srcInfo.keyChain === 'd') { return undefined; } else { return srcInfo.value; } }); // => { a: 'A', b: 'b', c: null } ``` You can operate the parent node object directly in converter. ```js var src = { a: 1, b: 2 }; var dst = {}; copyProps(src, dst, function(srcInfo, dstInfo) { Object.defineProperty(dstInfo.parent, dstInfo.key, { writable: false, enumerable: true, configurable: false, value: srcInfo.value * 2 }) }); // => { a: 2, b: 4 } dst // => { a: 2, b: 4 } dst.a = 9 dst // -> { a: 2, b: 4 } ``` ## API ### copyProps(src, dst [, fromto] [, converter] [, reverse]) => object Copy properties of *src* to *dst* deeply. If *fromto* is given, it is able to copy between different properties. If *converter* is given, it is able to convert the terminal values. #### Parameters: | Parameter | Type | Description | |:------------|:------:|:-------------------------------------------------| | *src* | object | A source object of copy. | | *dst* | object | A destinate object of copy. | | *fromto* | object | array | An object mapping properties between *src* and *dst*. (Optional) | | *converter* |function| A function to convert terminal values in *src*. (Optional) | | *reverse* |boolean | True, if copying reversively from dst to src and returns src object. `fromto` is also reversively used from value to key. This default value is `false`. (Optional) | #### Returns: *dst* object after copying. **Type:** object * **Format of fromto** *fromto* is a non-nested key-value object. And the *key*s are property key chains of *src* and the *value*s are property key chains of *dst*. The key chain is a string which is concatenated property keys on each level with dots, like `'aaa.bbb.ccc'`. The following example copys the value of `src.aaa.bbb.ccc` to `dst.xxx.yyy`. ```js copyProps(src, dst, { 'aaa.bbb.ccc' : 'xxx.yyy' }) ``` *fromto* can be an array. In that case, the array works as a map which has pairs of same key and value. * **API of converter** **converter(srcInfo, dstInfo) : Any** *converter* is a function to convert terminal values of propeerties of *src*. **Parameters:** | Parameter | Type | Description | |:------------|:------:|:---------------------------------------------| | *srcInfo* | object | An object which has informations about the current node of *src*. | | *dstInfo* | object | An object which has informations about the current node of *dst*. | **Return:** The converted value to be set as a destination property value. If this value is undefined, the destination property is not set to the destination node object. **Type:** *Any* * **Properties of srcInfo and dstInfo** *srcInfo* and *dstInfo* has same properties, as follows: | Property | Type | Description | |:-----------|:------:|:------------------------------------------| | *value* | *Any* | The value of the current node. | | *key* | string | The key name of the current node. | | *keyChain* | string | The full key of the current node concatenated with dot. | | *depth* | number | The depth of the current node. | | *parent* | object | The parent node of the current node. | ## License Copyright (C) 2016-2018 Takayuki Sato This program is free software under [MIT][mit-url] License. See the file LICENSE in this distribution for more details. [repo-url]: https://github.com/sttk/copy-props/ [npm-img]: https://img.shields.io/badge/npm-v2.0.4-blue.svg [npm-url]: https://www.npmjs.org/package/copy-props/ [mit-img]: https://img.shields.io/badge/license-MIT-green.svg [mit-url]: https://opensource.org/licenses.MIT [travis-img]: https://travis-ci.org/sttk/copy-props.svg?branch=master [travis-url]: https://travis-ci.org/sttk/copy-props [appveyor-img]: https://ci.appveyor.com/api/projects/status/github/sttk/copy-props?branch=master&svg=true [appveyor-url]: https://ci.appveyor.com/project/sttk/copy-props [coverage-img]: https://coveralls.io/repos/github/sttk/copy-props/badge.svg?branch=master [coverage-url]: https://coveralls.io/github/sttk/copy-props?branch=master