Skip to content

Commit c1656b1

Browse files
authored
Alias merge as concat for Set and Map collections (#1373)
BREAKING: Alias `merge` as `concat` for Set and Map collections
1 parent 550ba5c commit c1656b1

File tree

10 files changed

+118
-51
lines changed

10 files changed

+118
-51
lines changed

__tests__/List.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,12 +642,36 @@ describe('List', () => {
642642
});
643643

644644
it('concat works like Array.prototype.concat', () => {
645-
const v1 = List.of(1, 2, 3);
645+
const v1 = List([1, 2, 3]);
646646
const v2 = v1.concat(4, List([ 5, 6 ]), [7, 8], Seq([ 9, 10 ]), Set.of(11, 12), null as any);
647647
expect(v1.toArray()).toEqual([1, 2, 3]);
648648
expect(v2.toArray()).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, null]);
649649
});
650650

651+
it('concat returns self when no changes', () => {
652+
const v1 = List([1, 2, 3]);
653+
expect(v1.concat([])).toBe(v1);
654+
});
655+
656+
it('concat returns arg when concat to empty', () => {
657+
const v1 = List([1, 2, 3]);
658+
expect(List().concat(v1)).toBe(v1);
659+
});
660+
661+
it('concats a single value', () => {
662+
const v1 = List([1, 2, 3]);
663+
expect(v1.concat(4)).toEqual(List([1, 2, 3, 4]));
664+
});
665+
666+
it('concat returns List-coerced arg when concat to empty', () => {
667+
expect(List().concat([1, 2, 3])).toEqual(List([1, 2, 3]));
668+
});
669+
670+
it('concat does not spread in string characters', () => {
671+
const v1 = List([1, 2, 3]);
672+
expect(v1.concat('abcdef')).toEqual(List([1, 2, 3, 'abcdef']));
673+
});
674+
651675
it('allows chained mutations', () => {
652676
const v1 = List();
653677
const v2 = v1.push(1);

src/List.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from './TrieUtils';
2222
import { IndexedCollection } from './Collection';
2323
import { MapPrototype } from './Map';
24-
import { Iterator, iteratorValue, iteratorDone } from './Iterator';
24+
import { hasIterator, Iterator, iteratorValue, iteratorDone } from './Iterator';
2525

2626
import assertNotInfinite from './utils/assertNotInfinite';
2727

@@ -135,8 +135,28 @@ export class List extends IndexedCollection {
135135

136136
// @pragma Composition
137137

138-
merge(/*...collections*/) {
139-
return this.concat.apply(this, arguments);
138+
concat(/*...collections*/) {
139+
const seqs = [];
140+
for (let i = 0; i < arguments.length; i++) {
141+
const argument = arguments[i];
142+
const seq = IndexedCollection(
143+
typeof argument !== 'string' && hasIterator(argument)
144+
? argument
145+
: [argument]
146+
);
147+
if (seq.size !== 0) {
148+
seqs.push(seq);
149+
}
150+
}
151+
if (seqs.length === 0) {
152+
return this;
153+
}
154+
if (this.size === 0 && !this.__ownerID && seqs.length === 1) {
155+
return this.constructor(seqs[0]);
156+
}
157+
return this.withMutations(list => {
158+
seqs.forEach(seq => seq.forEach(value => list.push(value)));
159+
});
140160
}
141161

142162
setSize(size) {
@@ -215,6 +235,7 @@ const IS_LIST_SENTINEL = '@@__IMMUTABLE_LIST__@@';
215235
export const ListPrototype = List.prototype;
216236
ListPrototype[IS_LIST_SENTINEL] = true;
217237
ListPrototype[DELETE] = ListPrototype.remove;
238+
ListPrototype.merge = ListPrototype.concat;
218239
ListPrototype.setIn = MapPrototype.setIn;
219240
ListPrototype.deleteIn = ListPrototype.removeIn = MapPrototype.removeIn;
220241
ListPrototype.update = MapPrototype.update;

src/Map.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ MapPrototype[IS_MAP_SENTINEL] = true;
250250
MapPrototype[DELETE] = MapPrototype.remove;
251251
MapPrototype.removeIn = MapPrototype.deleteIn;
252252
MapPrototype.removeAll = MapPrototype.deleteAll;
253+
MapPrototype.concat = MapPrototype.merge;
253254
MapPrototype['@@transducer/init'] = MapPrototype.asMutable;
254255
MapPrototype['@@transducer/step'] = function(result, arr) {
255256
return result.set(arr[0], arr[1]);

src/Set.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ const IS_SET_SENTINEL = '@@__IMMUTABLE_SET__@@';
178178
const SetPrototype = Set.prototype;
179179
SetPrototype[IS_SET_SENTINEL] = true;
180180
SetPrototype[DELETE] = SetPrototype.remove;
181-
SetPrototype.merge = SetPrototype.union;
181+
SetPrototype.merge = SetPrototype.concat = SetPrototype.union;
182182
SetPrototype.withMutations = MapPrototype.withMutations;
183183
SetPrototype.asMutable = MapPrototype.asMutable;
184184
SetPrototype.asImmutable = MapPrototype.asImmutable;

type-definitions/Immutable.d.ts

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ declare module Immutable {
787787
/**
788788
* Returns a new List with other values or collections concatenated to this one.
789789
*
790-
* Note: `concat` *cannot* be safely used in `withMutations`.
790+
* Note: `concat` can be used in `withMutations`.
791791
*
792792
* @alias merge
793793
*/
@@ -1223,8 +1223,13 @@ declare module Immutable {
12231223
* ```
12241224
*
12251225
* Note: `merge` can be used in `withMutations`.
1226+
*
1227+
* @alias concat
12261228
*/
1227-
merge(...collections: Array<Iterable<[K, V]> | {[key: string]: V}>): this;
1229+
merge<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Map<K | KC, V | VC>;
1230+
merge<C>(...collections: Array<{[key: string]: C}>): Map<K | string, V | C>;
1231+
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Map<K | KC, V | VC>;
1232+
concat<C>(...collections: Array<{[key: string]: C}>): Map<K | string, V | C>;
12281233

12291234
/**
12301235
* Like `merge()`, `mergeWith()` returns a new Map resulting from merging
@@ -1512,12 +1517,6 @@ declare module Immutable {
15121517

15131518
// Sequence algorithms
15141519

1515-
/**
1516-
* Returns a new Map with other collections concatenated to this one.
1517-
*/
1518-
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Map<K | KC, V | VC>;
1519-
concat<C>(...collections: Array<{[key: string]: C}>): Map<K | string, V | C>;
1520-
15211520
/**
15221521
* Returns a new Map with values passed through a
15231522
* `mapper` function.
@@ -1628,14 +1627,34 @@ declare module Immutable {
16281627
*/
16291628
readonly size: number;
16301629

1631-
// Sequence algorithms
1632-
16331630
/**
1634-
* Returns a new OrderedMap with other collections concatenated to this one.
1631+
* Returns a new OrderedMap resulting from merging the provided Collections
1632+
* (or JS objects) into this OrderedMap. In other words, this takes each
1633+
* entry of each collection and sets it on this OrderedMap.
1634+
*
1635+
* Note: Values provided to `merge` are shallowly converted before being
1636+
* merged. No nested values are altered.
1637+
*
1638+
* <!-- runkit:activate -->
1639+
* ```js
1640+
* const { OrderedMap } = require('immutable@4.0.0-rc.7')
1641+
* const one = OrderedMap({ a: 10, b: 20, c: 30 })
1642+
* const two = OrderedMap({ b: 40, a: 50, d: 60 })
1643+
* one.merge(two) // OrderedMap { "a": 50, "b": 40, "c": 30, "d": 60 }
1644+
* two.merge(one) // OrderedMap { "b": 20, "a": 10, "d": 60, "c": 30 }
1645+
* ```
1646+
*
1647+
* Note: `merge` can be used in `withMutations`.
1648+
*
1649+
* @alias concat
16351650
*/
1651+
merge<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): OrderedMap<K | KC, V | VC>;
1652+
merge<C>(...collections: Array<{[key: string]: C}>): OrderedMap<K | string, V | C>;
16361653
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): OrderedMap<K | KC, V | VC>;
16371654
concat<C>(...collections: Array<{[key: string]: C}>): OrderedMap<K | string, V | C>;
16381655

1656+
// Sequence algorithms
1657+
16391658
/**
16401659
* Returns a new OrderedMap with values passed through a
16411660
* `mapper` function.
@@ -1811,24 +1830,26 @@ declare module Immutable {
18111830
*
18121831
* Note: `union` can be used in `withMutations`.
18131832
* @alias merge
1833+
* @alias concat
18141834
*/
1815-
union(...collections: Array<Iterable<T>>): this;
1816-
merge(...collections: Array<Iterable<T>>): this;
1835+
union<C>(...collections: Array<Iterable<C>>): Set<T | C>;
1836+
merge<C>(...collections: Array<Iterable<C>>): Set<T | C>;
1837+
concat<C>(...collections: Array<Iterable<C>>): Set<T | C>;
18171838

18181839
/**
18191840
* Returns a Set which has removed any values not also contained
18201841
* within `collections`.
18211842
*
18221843
* Note: `intersect` can be used in `withMutations`.
18231844
*/
1824-
intersect(...collections: Array<Collection<any, T> | Array<T>>): this;
1845+
intersect(...collections: Array<Iterable<T>>): this;
18251846

18261847
/**
18271848
* Returns a Set excluding any values contained within `collections`.
18281849
*
18291850
* Note: `subtract` can be used in `withMutations`.
18301851
*/
1831-
subtract(...collections: Array<Collection<any, T> | Array<T>>): this;
1852+
subtract(...collections: Array<Iterable<T>>): this;
18321853

18331854

18341855
// Transient changes
@@ -1863,11 +1884,6 @@ declare module Immutable {
18631884

18641885
// Sequence algorithms
18651886

1866-
/**
1867-
* Returns a new Set with other collections concatenated to this one.
1868-
*/
1869-
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Set<T | C>;
1870-
18711887
/**
18721888
* Returns a new Set with values passed through a
18731889
* `mapper` function.
@@ -1956,12 +1972,19 @@ declare module Immutable {
19561972
*/
19571973
readonly size: number;
19581974

1959-
// Sequence algorithms
1960-
19611975
/**
1962-
* Returns a new OrderedSet with other collections concatenated to this one.
1976+
* Returns an OrderedSet including any value from `collections` that does
1977+
* not already exist in this OrderedSet.
1978+
*
1979+
* Note: `union` can be used in `withMutations`.
1980+
* @alias merge
1981+
* @alias concat
19631982
*/
1964-
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): OrderedSet<T | C>;
1983+
union<C>(...collections: Array<Iterable<C>>): OrderedSet<T | C>;
1984+
merge<C>(...collections: Array<Iterable<C>>): OrderedSet<T | C>;
1985+
concat<C>(...collections: Array<Iterable<C>>): OrderedSet<T | C>;
1986+
1987+
// Sequence algorithms
19651988

19661989
/**
19671990
* Returns a new Set with values passed through a
@@ -3013,7 +3036,7 @@ declare module Immutable {
30133036
* All entries will be present in the resulting Seq, even if they
30143037
* are duplicates.
30153038
*/
3016-
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Seq.Set<T | C>;
3039+
concat<U>(...collections: Array<Iterable<U>>): Seq.Set<T | U>;
30173040

30183041
/**
30193042
* Returns a new Seq.Set with values passed through a
@@ -3717,7 +3740,7 @@ declare module Immutable {
37173740
/**
37183741
* Returns a new Collection with other collections concatenated to this one.
37193742
*/
3720-
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Collection.Set<T | C>;
3743+
concat<U>(...collections: Array<Iterable<U>>): Collection.Set<T | U>;
37213744

37223745
/**
37233746
* Returns a new Collection.Set with values passed through a

type-definitions/immutable.js.flow

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ declare class SetCollection<+T> extends Collection<T, T> {
401401
@@iterator(): Iterator<T>;
402402
toSeq(): SetSeq<T>;
403403

404-
concat<C>(...iters: Array<Iterable<C> | C>): SetCollection<T | C>;
404+
concat<U>(...collections: Iterable<U>[]): SetCollection<T | U>;
405405

406406
// `filter`, `map` and `flatMap` cannot be defined further up the hierarchy,
407407
// because the implementation for `KeyedCollection` allows the value type to
@@ -619,7 +619,7 @@ declare class SetSeq<+T> extends Seq<T, T> mixins SetCollection<T> {
619619

620620
// Override specialized return types
621621

622-
concat<C>(...iters: Array<Iterable<C> | C>): SetSeq<T | C>;
622+
concat<U>(...collections: Iterable<U>[]): SetSeq<T | U>;
623623

624624
filter(predicate: typeof Boolean): SetSeq<$NonMaybeType<T>>;
625625
filter(
@@ -836,6 +836,9 @@ declare class Map<K, +V> extends KeyedCollection<K, V> {
836836
merge<K_, V_>(
837837
...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
838838
): Map<K | K_, V | V_>;
839+
concat<K_, V_>(
840+
...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
841+
): Map<K | K_, V | V_>;
839842

840843
mergeWith<K_, W, X>(
841844
merger: (oldVal: V, newVal: W, key: K) => X,
@@ -883,9 +886,6 @@ declare class Map<K, +V> extends KeyedCollection<K, V> {
883886

884887
flip(): Map<V, K>;
885888

886-
concat<KC, VC>(...iters: Array<Iterable<[KC, VC]>>): Map<K | KC, V | VC>;
887-
concat<KC, VC>(...iters: Array<PlainObjInput<KC, VC>>): Map<K | KC, V | VC>;
888-
889889
filter(predicate: typeof Boolean): Map<K, $NonMaybeType<V>>;
890890
filter(
891891
predicate: (value: V, key: K, iter: this) => mixed,
@@ -937,6 +937,9 @@ declare class OrderedMap<K, +V> extends Map<K, V> {
937937
merge<K_, V_>(
938938
...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
939939
): OrderedMap<K | K_, V | V_>;
940+
concat<K_, V_>(
941+
...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
942+
): OrderedMap<K | K_, V | V_>;
940943

941944
mergeWith<K_, W, X>(
942945
merger: (oldVal: V, newVal: W, key: K) => X,
@@ -984,9 +987,6 @@ declare class OrderedMap<K, +V> extends Map<K, V> {
984987

985988
flip(): OrderedMap<V, K>;
986989

987-
concat<KC, VC>(...iters: Array<Iterable<[KC, VC]>>): OrderedMap<K | KC, V | VC>;
988-
concat<KC, VC>(...iters: Array<PlainObjInput<KC, VC>>): OrderedMap<K | KC, V | VC>;
989-
990990
filter(predicate: typeof Boolean): OrderedMap<K, $NonMaybeType<V>>;
991991
filter(
992992
predicate: (value: V, key: K, iter: this) => mixed,
@@ -1038,6 +1038,7 @@ declare class Set<+T> extends SetCollection<T> {
10381038
clear(): this;
10391039
union<U>(...collections: Iterable<U>[]): Set<T | U>;
10401040
merge<U>(...collections: Iterable<U>[]): Set<T | U>;
1041+
concat<U>(...collections: Iterable<U>[]): Set<T | U>;
10411042
intersect<U>(...collections: Iterable<U>[]): Set<T & U>;
10421043
subtract(...collections: Iterable<mixed>[]): this;
10431044

@@ -1048,8 +1049,6 @@ declare class Set<+T> extends SetCollection<T> {
10481049

10491050
// Override specialized return types
10501051

1051-
concat<C>(...iters: Array<Iterable<C> | C>): Set<T | C>;
1052-
10531052
filter(predicate: typeof Boolean): Set<$NonMaybeType<T>>;
10541053
filter(
10551054
predicate: (value: T, value: T, iter: this) => mixed,
@@ -1087,10 +1086,9 @@ declare class OrderedSet<+T> extends Set<T> {
10871086
add<U>(value: U): OrderedSet<T | U>;
10881087
union<U>(...collections: Iterable<U>[]): OrderedSet<T | U>;
10891088
merge<U>(...collections: Iterable<U>[]): OrderedSet<T | U>;
1089+
concat<U>(...collections: Iterable<U>[]): OrderedSet<T | U>;
10901090
intersect<U>(...collections: Iterable<U>[]): OrderedSet<T & U>;
10911091

1092-
concat<C>(...iters: Array<Iterable<C> | C>): OrderedSet<T | C>;
1093-
10941092
filter(predicate: typeof Boolean): OrderedSet<$NonMaybeType<T>>;
10951093
filter(
10961094
predicate: (value: T, value: T, iter: this) => mixed,

type-definitions/ts-tests/map.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,13 @@ import { Map, List } from '../../';
252252
// $ExpectType Map<string, number>
253253
Map<string, number>().merge({ a: 1 });
254254

255-
// $ExpectError
255+
// $ExpectType Map<string, number | { b: number; }>
256256
Map<string, number>().merge({ a: { b: 1 } });
257257

258258
// $ExpectType Map<number, number>
259259
Map<number, number>().merge(Map<number, number>());
260260

261-
// $ExpectError
261+
// $ExpectType Map<number, string | number>
262262
Map<number, number>().merge(Map<number, string>());
263263

264264
// $ExpectType Map<number, string | number>

type-definitions/ts-tests/ordered-map.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,13 @@ import { OrderedMap, List } from '../../';
252252
// $ExpectType OrderedMap<string, number>
253253
OrderedMap<string, number>().merge({ a: 1 });
254254

255-
// $ExpectError
255+
// $ExpectType OrderedMap<string, number | { b: number; }>
256256
OrderedMap<string, number>().merge({ a: { b: 1 } });
257257

258258
// $ExpectType OrderedMap<number, number>
259259
OrderedMap<number, number>().merge(OrderedMap<number, number>());
260260

261-
// $ExpectError
261+
// $ExpectType OrderedMap<number, string | number>
262262
OrderedMap<number, number>().merge(OrderedMap<number, string>());
263263

264264
// $ExpectType OrderedMap<number, string | number>

type-definitions/ts-tests/ordered-set.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ import { OrderedSet, Map } from '../../';
155155
// $ExpectType OrderedSet<number>
156156
OrderedSet<number>().union(OrderedSet<number>());
157157

158-
// $ExpectError
158+
// $ExpectType OrderedSet<string | number>
159159
OrderedSet<number>().union(OrderedSet<string>());
160160

161161
// $ExpectType OrderedSet<string | number>
@@ -170,7 +170,7 @@ import { OrderedSet, Map } from '../../';
170170
// $ExpectType OrderedSet<number>
171171
OrderedSet<number>().merge(OrderedSet<number>());
172172

173-
// $ExpectError
173+
// $ExpectType OrderedSet<string | number>
174174
OrderedSet<number>().merge(OrderedSet<string>());
175175

176176
// $ExpectType OrderedSet<string | number>

0 commit comments

Comments
 (0)