Skip to content

Commit 44e0b31

Browse files
author
sttk
committed
Use each-props and change API about reverse flag.
1 parent bfe754e commit 44e0b31

File tree

9 files changed

+829
-2141
lines changed

9 files changed

+829
-2141
lines changed

README.md

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Install
1515
-------
1616

1717
```
18-
$ npm i copy-props
18+
$ npm i copy-props --save
1919
```
2020

2121
Usage
@@ -58,11 +58,11 @@ Usage
5858
var src = { a: 1, b: { b1: 'bbb' } };
5959
var dst = { a: 0 };
6060

61-
copyProps(src, dst, function(value, keyChain) {
62-
if (keyChain === 'a') {
61+
copyProps(src, dst, function(value, keychain) {
62+
if (keychain === 'a') {
6363
return value * 2;
6464
}
65-
if (keyChain === 'b.b1') {
65+
if (keychain === 'b.b1') {
6666
return value.toUpperCase();
6767
}
6868
});
@@ -74,12 +74,12 @@ Usage
7474
```js
7575
var src = { a: 1, b: { c: 'CCC' }, d: { e: 'EEE' } };
7676
var dst = { a: 9, b: { c: 'xxx' }, d: { e: 'yyy' } };
77-
var fromToProps = [ 'a.b.c', 'd.e' ];
78-
copyProps(src, dst, fromToProps);
77+
var fromto = [ 'a.b.c', 'd.e' ];
78+
copyProps(src, dst, fromto);
7979
// => { a: 1, b: { c: 'CCC' }, d: { e: 'EEE' } }
8080
```
8181

82-
* Can copy reversively (from *dst* to *src*) by option `{ reverse: true }` (and return *src*):
82+
* Can copy reversively (from *dst* to *src*) by reverse flag (and return *src*):
8383

8484
```js
8585
var src = { a: 1, b: { b1: 'bbb' }, c: 'ccc' };
@@ -98,56 +98,48 @@ Usage
9898
'b.b1': 'f.b1',
9999
'b.b2': 'f.b2',
100100
'c': 'f.c',
101-
}, { reverse: true });
101+
}, true);
102102
// => { a: 2, b: { b1: 'bbb', b2: 'yyy' }, c: 'ccc', d: 'ddd' }
103103
```
104104

105-
* Can reject to copy null value by option `{ rejectNull: true }` :
105+
* 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.
106106

107107
```js
108-
var src = { a: 8, b: { c: -1, d: null } };
109-
var dst = { a: 1, b: { c: 2, e: 4 } };
110-
111-
copyProps(src, dst, function(v) {
112-
return (typeof v !== 'number' || v < 0) ? null : v;
113-
}, { rejectNull: true });
114-
// => { a: 8, b: { c: 2, e: 4 } }
115-
```
116-
117-
***This `opts` must be passed at the place of the 4rd argument or later.***
118-
119-
So, when you use the `opts` and don't use neither `fromToProps` nor `converter`, write the code as follows:
120-
121-
```js
122-
var src = { a: 8, b: { c: -1, d: null } };
123-
var dst = { a: 1, b: { c: 2, e: 4 } };
124-
125-
copyProps(src, dst, null, { rejectNull: true });
126-
// => { a: 8, b: { c: -1, e: 4 } }
108+
var src = { a: 'A', b: undefined, c: null, d: 1 };
109+
var dst = { a: 'a', b: 'b', c: 'c' };
110+
111+
copyProps(src, dst, function(value, key) {
112+
if (key === 'd') {
113+
return undefined;
114+
} else {
115+
return value;
116+
}
117+
});
118+
// => { a: 'A', b: 'b', c: null }
127119
```
128120

129121
API
130122
---
131123

132-
### <u>copyProps(src, dst [, fromToProps] [, converter] [, opts]) => object</u>
124+
### <u>copyProps(src, dst [, fromto] [, converter] [, reverse]) => object</u>
133125

134126
Copy properties of *src* to *dst* deeply.
135-
If *fromToProps* is given, it is able to copy between different properties.
127+
If *fromto* is given, it is able to copy between different properties.
136128
If *converter* is given, it is able to convert the terminal values.
137129

138130
* **Arguments:**
139131

140132
* **src** [object] : a source object of copy.
141133
* **dst** [object] : a destinate object of copy.
142-
* **fromToProps** [object | array] : an object mapping properties between *src* and *dst*. (optional)
134+
* **fromto** [object | array] : an object mapping properties between *src* and *dst*. (optional)
143135
* **converter** [function] : a function to convert terminal values in *src*. (optional)
144-
* **opts** [object] : an option object which specifys copy's behaviors (optional)
136+
* **reverse** [boolean] : copys reversively from dst to src and returns src object. `fromto` is also reversively used from value to key. This default value is `false`. (optional)
145137

146138
* **Return** [object] : *dst* object after copying.
147139

148-
#### *Format of fromToProps*
140+
#### *Format of fromto*
149141

150-
*fromToProps* 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*.
142+
*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*.
151143
The key chain is a string which is concatenated property keys on each level with dots, like `'aaa.bbb.ccc'`.
152144

153145
The following example copys the value of `src.aaa.bbb.ccc` to `dst.xxx.yyy`.
@@ -158,29 +150,21 @@ copyProps(src, dst, {
158150
})
159151
```
160152

161-
*fromToProps* can be an array. In that case, the array works as a map which has pairs of same key and value.
153+
*fromto* can be an array. In that case, the array works as a map which has pairs of same key and value.
162154

163155
#### *API of converter*
164156

165-
**<u>converter(value, keyChain) => any</u>**
157+
**<u>converter(value, keychain) => any</u>**
166158

167159
*converter* is a function to convert terminal values of propeerties of *src*.
168160

169161
* **Arguments:**
170162

171163
* **value** [any] : a property value to be converted.
172-
* **keyChain** [string] : a string of property keys concatenated with dots.
164+
* **keychain** [string] : a string of property keys concatenated with dots.
173165

174166
* **Return:** [any] : converted value.
175167

176-
#### *Properties of opts*
177-
178-
*opts* can have following properties:
179-
180-
* **reverse** [boolean] : Copys reversively from *dst* to *src* and returns *src* object. *fromToProps* is also reversively used from value to key. This default value is `false`.
181-
* **rejectNull** [boolean] : if this option is `true`, a value of a source object or a return value of *converter* is not copied when the value is null. The default value is `true`.
182-
183-
184168
License
185169
-------
186170

index.js

Lines changed: 141 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,167 @@
44

55
var inspect = require('util').inspect;
66
var isPlainObject = require('lodash.isplainobject');
7-
var copyProps = require('./lib/copy-props');
7+
var eachProps = require('each-props');
8+
var setDeep = require('lodash.set');
89

9-
module.exports = function(src, dst, fromTo, converter, opts) {
10+
module.exports = function(src, dst, fromto, converter, reverse) {
1011
if (!isPlainObject(src)) {
11-
throw new TypeError('The 1st argument need to be a plain object: ' +
12+
throw new TypeError('The source needs to be a plain object: ' +
1213
inspect(src));
1314
}
1415

1516
if (!isPlainObject(dst)) {
16-
throw new TypeError('The 2nd argument need to be a plain object: ' +
17+
throw new TypeError('The destination needs to be a plain object: ' +
1718
inspect(dst));
1819
}
1920

20-
if (opts == null) {
21-
if (isPlainObject(converter)) {
22-
opts = converter;
23-
24-
if (typeof fromTo === 'function') {
25-
converter = fromTo;
26-
fromTo = null;
21+
switch (typeof fromto) {
22+
case 'boolean': {
23+
reverse = fromto;
24+
converter = noop;
25+
fromto = undefined;
26+
break;
27+
}
28+
case 'function': {
29+
reverse = converter;
30+
converter = fromto;
31+
fromto = undefined;
32+
break;
33+
}
34+
default: {
35+
if (fromto == null) {
36+
/*fromto = fromto;*/
37+
} else if (isPlainObject(fromto)) {
38+
/*fromto = fromto;*/
39+
} else if (Array.isArray(fromto)) {
40+
fromto = arrayToMap(fromto);
2741
} else {
28-
converter = null;
42+
throw new TypeError('The from-to map needs to be a plain object: ' +
43+
inspect(fromto));
2944
}
45+
break;
46+
}
47+
}
3048

31-
} else if (converter == null) {
49+
switch (typeof converter) {
50+
case 'function': {
51+
break;
52+
}
53+
case 'boolean': {
54+
reverse = converter;
55+
converter = noop;
56+
break;
57+
}
58+
default: {
59+
if (converter == null) {
60+
converter = noop;
61+
} else {
62+
throw new TypeError('The converter needs to be a function: ' +
63+
inspect(converter));
64+
}
65+
}
66+
}
3267

33-
if (typeof fromTo === 'function') {
34-
converter = fromTo;
35-
fromTo = null;
68+
switch (typeof reverse) {
69+
case 'boolean': {
70+
break;
71+
}
72+
default: {
73+
if (reverse == null) {
74+
reverse = false;
75+
} else {
76+
throw new TypeError('The reverse flag needs to be a boolean: ' +
77+
inspect(reverse));
3678
}
3779
}
3880
}
3981

40-
if (opts != null && !isPlainObject(opts)) {
41-
throw new TypeError('The 5th argument need to be an object: ' +
42-
inspect(opts));
82+
return copyProps(src, dst, fromto, converter, reverse);
83+
};
84+
85+
function noop(v) {
86+
return v;
87+
}
88+
89+
function invert(fromto) {
90+
var inverted = {};
91+
var keys = Object.keys(fromto);
92+
for (var i = 0, n = keys.length; i < n; i++) {
93+
var key = keys[i];
94+
var val = fromto[key];
95+
if (!inverted[val]) {
96+
inverted[val] = [];
97+
}
98+
inverted[val].push(key);
4399
}
100+
return inverted;
101+
}
44102

45-
if (converter != null && typeof converter !== 'function') {
46-
throw new TypeError('The 4th argument need to be a function: ' +
47-
inspect(converter));
103+
function arrayToMap(array) {
104+
var map = {};
105+
for (var i = 0, n = array.length; i < n; i++) {
106+
map[array[i]] = array[i];
48107
}
108+
return map;
109+
}
49110

50-
if (fromTo != null && !isPlainObject(fromTo) && !Array.isArray(fromTo)) {
51-
throw new TypeError('The 3th argument need to be a plain object or ' +
52-
'an array: ' + inspect(fromTo));
111+
function copyProps(src, dst, fromto, converter, reverse) {
112+
if (reverse) {
113+
var tmp = src;
114+
src = dst;
115+
dst = tmp;
53116
}
54117

55-
return copyProps(src, dst, fromTo, converter, opts);
56-
};
118+
if (isPlainObject(fromto)) {
119+
if (reverse) {
120+
fromto = invert(fromto);
121+
122+
eachProps(src, function(value, keychain, opts) {
123+
if (isPlainObject(value)) {
124+
return;
125+
}
126+
var dstKeychains = fromto[keychain];
127+
if (dstKeychains == null) {
128+
return;
129+
}
130+
for (var i = 0, n = dstKeychains.length; i < n; i++) {
131+
var dstValue = converter(value, keychain, dstKeychains[i], opts);
132+
if (dstValue === undefined) {
133+
return;
134+
}
135+
setDeep(dst, dstKeychains[i], dstValue);
136+
}
137+
});
138+
return dst;
139+
}
140+
141+
eachProps(src, function(value, keychain, opts) {
142+
if (isPlainObject(value)) {
143+
return;
144+
}
145+
var dstKeychain = fromto[keychain];
146+
if (!dstKeychain) {
147+
return;
148+
}
149+
var dstValue = converter(value, keychain, dstKeychain, opts);
150+
if (dstValue === undefined) {
151+
return;
152+
}
153+
setDeep(dst, dstKeychain, dstValue);
154+
});
155+
return dst;
156+
157+
} else {
158+
eachProps(src, function(value, keychain, opts) {
159+
if (isPlainObject(value)) {
160+
return;
161+
}
162+
var dstValue = converter(value, keychain, keychain, opts);
163+
if (dstValue === undefined) {
164+
return;
165+
}
166+
setDeep(dst, keychain, dstValue);
167+
});
168+
return dst;
169+
}
170+
}

0 commit comments

Comments
 (0)