8000 Intellegently convert js values to immutable collections when deep me… · spmjs/immutable-js@1a707cb · GitHub
[go: up one dir, main page]

Skip to content

Commit 1a707cb

Browse files
committed
Intellegently convert js values to immutable collections when deep merging
A common pattern is passing JS values to mergeDeep with the expectation that they will be converted similarly to `fromJS`, however this wasn't the case, and the conversion was instead based on the immutable collection being merged into. This changes the behavior to allow that use case, and is now documented on the `merge` function and applies to the whole merge family.
1 parent 4b27aa3 commit 1a707cb

File tree

10 files changed

+155
-101
lines changed

10 files changed

+155
-101
lines changed

__tests__/merge.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,36 @@ describe('merge', () => {
8686
);
8787
})
8888

89+
it('can overwrite existing maps with objects', () => {
90+
var m1 = I.fromJS({ a: { x: 1, y: 1 } }); // deep conversion.
91+
var m2 = I.Map({ a: { z: 10 } }); // shallow conversion to Map.
92+
93+
// raw object simply replaces map.
94+
expect(m1.merge(m2).get('a')).toEqual({z: 10}) // raw object.
95+
expect(m1.mergeDeep(m2).get('a')).toEqual({z: 10}) // raw object.
96+
})
97+
98+
it('merges map entries with Vector values', () => {
99+
expect(
100+
I.fromJS({a:[1]}).merge({b:[2]})
101+
).is(I.fromJS(
102+
{a:[1], b:[2]}
103+
));
104+
expect(
105+
I.fromJS({a:[1]}).mergeDeep({b:[2]})
106+
).is(I.fromJS(
107+
{a:[1], b:[2]}
108+
));
109+
})
110+
111+
it('maintains JS values inside immutable collections', () => {
112+
var m1 = I.fromJS({a:{b:[{imm:'map'}]}});
113+
var m2 = m1.mergeDeep(
114+
I.Map({a: I.Map({b: I.List.of( {plain:'obj'} )})})
115+
);
116+
117+
expect(m1.getIn(['a', 'b', 0])).is(I.Map([['imm', 'map']]));
118+
expect(m2.getIn(['a', 'b', 0])).toEqual({plain: 'obj'});
119+
})
120+
89121
})

dist/immutable.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,12 @@ declare module 'immutable' {
13371337
* (or JS objects) into this Map. In other words, this takes each entry of
13381338
* each iterable and sets it on this Map.
13391339
*
1340+
* If any of the values provided to `merge` are not Iterable (would return
1341+
* false for `Immutable.isIterable`) then they are deeply converted via
1342+
* `Immutable.fromJS` before being merged. However, if the value is an
1343+
* Iterable but contains non-iterable JS objects or arrays, those nested
1344+
* values will be preserved.
1345+
*
13401346
* var x = Immutable.Map({a: 10, b: 20, c: 30});
13411347
* va B41A r y = Immutable.Map({b: 40, a: 50, d: 60});
13421348
* x.merge(y) // { a: 50, b: 40, c: 30, d: 60 }

dist/immutable.js

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,31 @@ function seqIterator(seq, type, reverse, useKeys) {
11411141
}
11421142
return seq.__iteratorUncached(type, reverse);
11431143
}
1144+
function fromJS(json, converter) {
1145+
if (converter) {
1146+
return _fromJSWith(converter, json, '', {'': json});
1147+
}
1148+
return _fromJSDefault(json);
1149+
}
1150+
function _fromJSWith(converter, json, key, parentJSON) {
1151+
if (Array.isArray(json) || isPlainObj(json)) {
1152+
return converter.call(parentJSON, key, Iterable(json).map((function(v, k) {
1153+
return _fromJSWith(converter, v, k, json);
1154+
})));
1155+
}
1156+
return json;
1157+
}
1158+
function _fromJSDefault(json) {
1159+
if (json && typeof json === 'object') {
1160+
if (Array.isArray(json)) {
1161+
return Iterable(json).map(_fromJSDefault).toList();
1162+
}
1163+
if (json.constructor === Object) {
1164+
return Iterable(json).map(_fromJSDefault).toMap();
1165+
}
1166+
}
1167+
return json;
1168+
}
11441169
var Collection = function Collection() {
11451170
throw TypeError('Abstract');
11461171
};
@@ -1167,7 +1192,7 @@ Collection.Keyed = KeyedCollection;
11671192
Collection.Indexed = IndexedCollection;
11681193
Collection.Set = SetCollection;
11691194
var Map = function Map(value) {
1170-
return arguments.length === 0 ? emptyMap() : value && value.constructor === $Map ? value : emptyMap().merge(value);
1195+
return arguments.length === 0 ? emptyMap() : value && value.constructor === $Map ? value : emptyMap().merge(KeyedIterable(value));
11711196
};
11721197
var $Map = Map;
11731198
($traceurRuntime.createClass)(Map, {
@@ -1625,19 +1650,20 @@ function expandNodes(ownerID, nodes, bitmap, including, node) {
16251650
function mergeIntoMapWith(map, merger, iterables) {
16261651
var iters = [];
16271652
for (var ii = 0; ii < iterables.length; ii++) {
1628-
iterables[ii] && iters.push(KeyedIterable(iterables[ii]));
1653+
var value = iterables[ii];
1654+
var iter = KeyedIterable(value);
1655+
if (!isIterable(value)) {
1656+
iter = iter.map((function(v) {
1657+
return fromJS(v);
1658+
}));
1659+
}
1660+
iters.push(iter);
16291661
}
16301662
return mergeIntoCollectionWith(map, merger, iters);
16311663
}
16321664
function deepMerger(merger) {
16331665
return (function(existing, value) {
1634-
if (existing && existing.mergeDeepWith) {
1635-
var iterable = isIterable(value) ? value : maybeSeqFromValue(value, true);
1636-
if (iterable) {
1637-
return existing.mergeDeepWith(merger, iterable);
1638-
}
1639-
}
1640-
return merger ? merger(existing, value) : value;
1666+
return existing && existing.mergeDeepWith && isIterable(value) ? existing.mergeDeepWith(merger, value) : merger ? merger(existing, value) : value;
16411667
});
16421668
}
16431669
function mergeIntoCollectionWith(collection, merger, iters) {
@@ -2342,16 +2368,13 @@ var List = function List(value) {
23422368
if (value && value.constructor === $List) {
23432369
return value;
23442370
}
2345-
var isArray = Array.isArray(value);
2346-
if (!isArray) {
2347-
value = Iterable(value);
2348-
}
2349-
var size = isArray ? value.length : value.size;
2371+
value = Iterable(value);
2372+
var size = value.size;
23502373
if (size === 0) {
23512374
return empty;
23522375
}
23532376
if (size > 0 && size < SIZE) {
2354-
return makeList(0, size, SHIFT, null, new VNode(isArray ? arrCopy(value) : value.toArray()));
2377+
return makeList(0, size, SHIFT, null, new VNode(value.toArray()));
23552378
}
23562379
return empty.merge(value);
23572380
};
@@ -2824,13 +2847,20 @@ function setListBounds(list, begin, end) {
28242847
}
28252848
function mergeIntoListWith(list, merger, iterables) {
28262849
var iters = [];
2850+
var maxSize = 0;
28272851
for (var ii = 0; ii < iterables.length; ii++) {
2828-
var iter = iterables[ii];
2829-
iter && iters.push(Iterable(iter));
2852+
var value = iterables[ii];
2853+
var iter = Iterable(value);
2854+
if (iter.size > maxSize) {
2855+
maxSize = iter.size;
2856+
}
2857+
if (!isIterable(value)) {
2858+
iter = iter.map((function(v) {
2859+
return fromJS(v);
2860+
}));
2861+
}
2862+
iters.push(iter);
28302863
}
2831-
var maxSize = Math.max.apply(Math, iters.map((function(s) {
2832-
return s.size || 0;
2833-
})));
28342864
if (maxSize > list.size) {
28352865
list = list.setSize(maxSize);
28362866
}
@@ -3179,7 +3209,7 @@ function emptySet() {
31793209
return EMPTY_SET || (EMPTY_SET = makeSet(emptyMap()));
31803210
}
31813211
var OrderedMap = function OrderedMap(value) {
3182-
return arguments.length === 0 ? emptyOrderedMap() : value && value.constructor === $OrderedMap ? value : emptyOrderedMap().merge(value);
3212+
return arguments.length === 0 ? emptyOrderedMap() : value && value.constructor === $OrderedMap ? value : emptyOrderedMap().merge(KeyedIterable(value));
31833213
};
31843214
var $OrderedMap = OrderedMap;
31853215
($traceurRuntime.createClass)(OrderedMap, {
@@ -3285,7 +3315,7 @@ var Record = function Record(defaultValues, name) {
32853315
if (!(this instanceof RecordType)) {
32863316
return new RecordType(values);
32873317
}
3288-
this._map = Map(values);
3318+
this._map = arguments.length === 0 ? Map() : Map(values);
32893319
};
32903320
var keys = Object.keys(defaultValues);
32913321
var RecordTypePrototype = RecordType.prototype = Object.create(RecordPrototype);
@@ -3572,31 +3602,6 @@ RepeatPrototype.take = RangePrototype.take;
35723602
RepeatPrototype.skip = RangePrototype.skip;
35733603
RepeatPrototype.__toJS = RangePrototype.__toJS;
35743604
var EMPTY_REPEAT;
3575-
function fromJS(json, converter) {
3576-
if (converter) {
3577-
return _fromJSWith(converter, json, '', {'': json});
3578-
}
3579-
return _fromJSDefault(json);
3580-
}
3581-
function _fromJSWith(converter, json, key, parentJSON) {
3582-
if (Array.isArray(json) || isPlainObj(json)) {
3583-
return converter.call(parentJSON, key, Iterable(json).map((function(v, k) {
3584-
return _fromJSWith(converter, v, k, json);
3585-
})));
3586-
}
3587-
return json;
3588-
}
3589-
function _fromJSDefault(json) {
3590-
if (json && typeof json === 'object') {
3591-
if (Array.isArray(json)) {
3592-
return Iterable(json).map(_fromJSDefault).toList();
3593-
}
3594-
if (json.constructor === Object) {
3595-
return Iterable(json).map(_fromJSDefault).toMap();
3596-
}
3597-
}
3598-
return json;
3599-
}
36003605
var Immutable = {
36013606
Iterable: Iterable,
36023607
Seq: Seq,

0 commit comments

Comments
 (0)
0