package/package.json 000644 0000002647 13276012113 011572 0 ustar 00 000000 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.js 000644 0000010535 13276012113 010744 0 ustar 00 000000 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/LICENSE 000644 0000002056 13276012113 010303 0 ustar 00 000000 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.md 000644 0000015612 13276012113 010557 0 ustar 00 000000 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