immutable 4.0.0-rc.6 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +375 -195
- package/dist/immutable.d.ts +1836 -881
- package/dist/immutable.es.js +1260 -883
- package/dist/immutable.js +5193 -4793
- package/dist/immutable.js.flow +1104 -269
- package/dist/immutable.min.js +53 -35
- package/package.json +8 -98
- package/contrib/cursor/README.md +0 -43
- package/contrib/cursor/__tests__/Cursor.ts.skip +0 -395
- package/contrib/cursor/index.d.ts +0 -287
- package/contrib/cursor/index.js +0 -360
- package/dist/immutable-nonambient.d.ts +0 -4677
package/README.md
CHANGED
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
Immutable collections for JavaScript
|
|
2
|
-
====================================
|
|
1
|
+
# Immutable collections for JavaScript
|
|
3
2
|
|
|
4
|
-
[](https://github.com/immutable-js/immutable-js/actions/workflows/ci.yml?query=branch%3Amain) [Chat on slack](https://immutable-js.slack.com)
|
|
4
|
+
|
|
5
|
+
[Read the docs](https://immutable-js.com) and eat your vegetables.
|
|
6
|
+
|
|
7
|
+
Docs are automatically generated from [README.md][] and [immutable.d.ts][].
|
|
8
|
+
Please contribute! Also, don't miss the [wiki][] which contains articles on
|
|
9
|
+
additional specific topics. Can't find something? Open an [issue][].
|
|
10
|
+
|
|
11
|
+
**Table of contents:**
|
|
12
|
+
|
|
13
|
+
- [Introduction](#introduction)
|
|
14
|
+
- [Getting started](#getting-started)
|
|
15
|
+
- [The case for Immutability](#the-case-for-immutability)
|
|
16
|
+
- [JavaScript-first API](#javaScript-first-api)
|
|
17
|
+
- [Nested Structures](#nested-structures)
|
|
18
|
+
- [Equality treats Collections as Values](#equality-treats-collections-as-values)
|
|
19
|
+
- [Batching Mutations](#batching-mutations)
|
|
20
|
+
- [Lazy Seq](#lazy-seq)
|
|
21
|
+
- [Additional Tools and Resources](#additional-tools-and-resources)
|
|
22
|
+
- [Contributing](#contributing)
|
|
23
|
+
|
|
24
|
+
## Introduction
|
|
5
25
|
|
|
6
26
|
[Immutable][] data cannot be changed once created, leading to much simpler
|
|
7
27
|
application development, no defensive copying, and enabling advanced memoization
|
|
@@ -22,16 +42,18 @@ intermediate representations. Create some `Seq` with `Range` and `Repeat`.
|
|
|
22
42
|
|
|
23
43
|
Want to hear more? Watch the presentation about Immutable.js:
|
|
24
44
|
|
|
25
|
-
|
|
45
|
+
[](https://youtu.be/I7IdS-PbEgI)
|
|
26
46
|
|
|
27
|
-
[
|
|
28
|
-
[
|
|
29
|
-
[
|
|
30
|
-
[
|
|
47
|
+
[README.md]: https://github.com/immutable-js/immutable-js/blob/main/README.md
|
|
48
|
+
[immutable.d.ts]: https://github.com/immutable-js/immutable-js/blob/main/type-definitions/immutable.d.ts
|
|
49
|
+
[wiki]: https://github.com/immutable-js/immutable-js/wiki
|
|
50
|
+
[issue]: https://github.com/immutable-js/immutable-js/issues
|
|
51
|
+
[Persistent]: https://en.wikipedia.org/wiki/Persistent_data_structure
|
|
52
|
+
[Immutable]: https://en.wikipedia.org/wiki/Immutable_object
|
|
53
|
+
[hash maps tries]: https://en.wikipedia.org/wiki/Hash_array_mapped_trie
|
|
54
|
+
[vector tries]: https://hypirion.com/musings/understanding-persistent-vector-pt-1
|
|
31
55
|
|
|
32
|
-
|
|
33
|
-
Getting started
|
|
34
|
-
---------------
|
|
56
|
+
## Getting started
|
|
35
57
|
|
|
36
58
|
Install `immutable` using npm.
|
|
37
59
|
|
|
@@ -39,56 +61,67 @@ Install `immutable` using npm.
|
|
|
39
61
|
npm install immutable
|
|
40
62
|
```
|
|
41
63
|
|
|
64
|
+
Or install using yarn.
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
yarn add immutable
|
|
68
|
+
```
|
|
69
|
+
|
|
42
70
|
Then require it into any module.
|
|
43
71
|
|
|
44
72
|
<!-- runkit:activate -->
|
|
73
|
+
|
|
45
74
|
```js
|
|
46
|
-
const { Map } = require('immutable')
|
|
47
|
-
const map1 = Map({ a: 1, b: 2, c: 3 })
|
|
48
|
-
const map2 = map1.set('b', 50)
|
|
49
|
-
map1.get('b') +
|
|
75
|
+
const { Map } = require('immutable');
|
|
76
|
+
const map1 = Map({ a: 1, b: 2, c: 3 });
|
|
77
|
+
const map2 = map1.set('b', 50);
|
|
78
|
+
map1.get('b') + ' vs. ' + map2.get('b'); // 2 vs. 50
|
|
50
79
|
```
|
|
51
80
|
|
|
52
81
|
### Browser
|
|
53
82
|
|
|
54
|
-
|
|
55
|
-
or use a CDN such as [CDNJS](https://cdnjs.com/libraries/immutable)
|
|
56
|
-
or [jsDelivr](http://www.jsdelivr.com/#!immutable.js).
|
|
83
|
+
Immutable.js has no dependencies, which makes it predictable to include in a Browser.
|
|
57
84
|
|
|
58
|
-
|
|
85
|
+
It's highly recommended to use a module bundler like [webpack](https://webpack.github.io/),
|
|
86
|
+
[rollup](https://rollupjs.org/), or
|
|
87
|
+
[browserify](https://browserify.org/). The `immutable` npm module works
|
|
88
|
+
without any additional consideration. All examples throughout the documentation
|
|
89
|
+
will assume use of this kind of tool.
|
|
90
|
+
|
|
91
|
+
Alternatively, Immutable.js may be directly included as a script tag. Download
|
|
92
|
+
or link to a CDN such as [CDNJS](https://cdnjs.com/libraries/immutable)
|
|
93
|
+
or [jsDelivr](https://www.jsdelivr.com/package/npm/immutable).
|
|
94
|
+
|
|
95
|
+
Use a script tag to directly add `Immutable` to the global scope:
|
|
59
96
|
|
|
60
97
|
```html
|
|
61
98
|
<script src="immutable.min.js"></script>
|
|
62
99
|
<script>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
100
|
+
var map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
|
|
101
|
+
var map2 = map1.set('b', 50);
|
|
102
|
+
map1.get('b'); // 2
|
|
103
|
+
map2.get('b'); // 50
|
|
67
104
|
</script>
|
|
68
105
|
```
|
|
69
106
|
|
|
70
|
-
Or use an AMD loader (such as [RequireJS](
|
|
107
|
+
Or use an AMD-style loader (such as [RequireJS](https://requirejs.org/)):
|
|
71
108
|
|
|
72
109
|
```js
|
|
73
110
|
require(['./immutable.min.js'], function (Immutable) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
111
|
+
var map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
|
|
112
|
+
var map2 = map1.set('b', 50);
|
|
113
|
+
map1.get('b'); // 2
|
|
114
|
+
map2.get('b'); // 50
|
|
78
115
|
});
|
|
79
116
|
```
|
|
80
117
|
|
|
81
|
-
If you're using [webpack](https://webpack.github.io/) or
|
|
82
|
-
[browserify](http://browserify.org/), the `immutable` npm module also works
|
|
83
|
-
from the browser.
|
|
84
|
-
|
|
85
118
|
### Flow & TypeScript
|
|
86
119
|
|
|
87
120
|
Use these Immutable collections and sequences as you would use native
|
|
88
|
-
collections in your [Flowtype](https://flowtype.org/) or [TypeScript](
|
|
121
|
+
collections in your [Flowtype](https://flowtype.org/) or [TypeScript](https://typescriptlang.org) programs while still taking
|
|
89
122
|
advantage of type generics, error detection, and auto-complete in your IDE.
|
|
90
123
|
|
|
91
|
-
Installing `immutable` via npm brings with it type definitions for Flow (v0.
|
|
124
|
+
Installing `immutable` via npm brings with it type definitions for Flow (v0.55.0 or higher)
|
|
92
125
|
and TypeScript (v2.1.0 or higher), so you shouldn't need to do anything at all!
|
|
93
126
|
|
|
94
127
|
#### Using TypeScript with Immutable.js v4
|
|
@@ -100,11 +133,12 @@ lib. Include either `"target": "es2015"` or `"lib": "es2015"` in your
|
|
|
100
133
|
`tsc` command.
|
|
101
134
|
|
|
102
135
|
<!-- runkit:activate -->
|
|
136
|
+
|
|
103
137
|
```js
|
|
104
|
-
const { Map } = require(
|
|
138
|
+
const { Map } = require('immutable');
|
|
105
139
|
const map1 = Map({ a: 1, b: 2, c: 3 });
|
|
106
140
|
const map2 = map1.set('b', 50);
|
|
107
|
-
map1.get('b') +
|
|
141
|
+
map1.get('b') + ' vs. ' + map2.get('b'); // 2 vs. 50
|
|
108
142
|
```
|
|
109
143
|
|
|
110
144
|
#### Using TypeScript with Immutable.js v3 and earlier:
|
|
@@ -114,17 +148,15 @@ via relative path to the type definitions at the top of your file.
|
|
|
114
148
|
|
|
115
149
|
```js
|
|
116
150
|
///<reference path='./node_modules/immutable/dist/immutable.d.ts'/>
|
|
117
|
-
import Immutable from
|
|
151
|
+
import Immutable from 'immutable';
|
|
118
152
|
var map1: Immutable.Map<string, number>;
|
|
119
|
-
map1 = Immutable.Map({a:1, b:2, c:3});
|
|
153
|
+
map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
|
|
120
154
|
var map2 = map1.set('b', 50);
|
|
121
155
|
map1.get('b'); // 2
|
|
122
156
|
map2.get('b'); // 50
|
|
123
157
|
```
|
|
124
158
|
|
|
125
|
-
|
|
126
|
-
The case for Immutability
|
|
127
|
-
-------------------------
|
|
159
|
+
## The case for Immutability
|
|
128
160
|
|
|
129
161
|
Much of what makes application development difficult is tracking mutation and
|
|
130
162
|
maintaining state. Developing with immutable data encourages you to think
|
|
@@ -143,23 +175,22 @@ and especially well with an application designed using the ideas of [Flux][].
|
|
|
143
175
|
When data is passed from above rather than being subscribed to, and you're only
|
|
144
176
|
interested in doing work when something has changed, you can use equality.
|
|
145
177
|
|
|
146
|
-
Immutable collections should be treated as
|
|
178
|
+
Immutable collections should be treated as _values_ rather than _objects_. While
|
|
147
179
|
objects represent some thing which could change over time, a value represents
|
|
148
180
|
the state of that thing at a particular instance of time. This principle is most
|
|
149
181
|
important to understanding the appropriate use of immutable data. In order to
|
|
150
182
|
treat Immutable.js collections as values, it's important to use the
|
|
151
|
-
`Immutable.is()` function or `.equals()` method to determine
|
|
152
|
-
instead of the `===` operator which determines object
|
|
183
|
+
`Immutable.is()` function or `.equals()` method to determine _value equality_
|
|
184
|
+
instead of the `===` operator which determines object _reference identity_.
|
|
153
185
|
|
|
154
186
|
<!-- runkit:activate -->
|
|
187
|
+
|
|
155
188
|
```js
|
|
156
|
-
const { Map } = require('immutable')
|
|
157
|
-
const map1 = Map(
|
|
158
|
-
const map2 =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const map3 = map1.set('b', 50)
|
|
162
|
-
assert.notEqual(map1, map3) // uses map1.equals
|
|
189
|
+
const { Map } = require('immutable');
|
|
190
|
+
const map1 = Map({ a: 1, b: 2, c: 3 });
|
|
191
|
+
const map2 = Map({ a: 1, b: 2, c: 3 });
|
|
192
|
+
map1.equals(map2); // true
|
|
193
|
+
map1 === map2; // false
|
|
163
194
|
```
|
|
164
195
|
|
|
165
196
|
Note: As a performance optimization Immutable.js attempts to return the existing
|
|
@@ -170,34 +201,43 @@ which would prefer to re-run the function if a deeper equality check could
|
|
|
170
201
|
potentially be more costly. The `===` equality check is also used internally by
|
|
171
202
|
`Immutable.is` and `.equals()` as a performance optimization.
|
|
172
203
|
|
|
204
|
+
<!-- runkit:activate -->
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
const { Map } = require('immutable');
|
|
208
|
+
const map1 = Map({ a: 1, b: 2, c: 3 });
|
|
209
|
+
const map2 = map1.set('b', 2); // Set to same value
|
|
210
|
+
map1 === map2; // true
|
|
211
|
+
```
|
|
212
|
+
|
|
173
213
|
If an object is immutable, it can be "copied" simply by making another reference
|
|
174
214
|
to it instead of copying the entire object. Because a reference is much smaller
|
|
175
215
|
than the object itself, this results in memory savings and a potential boost in
|
|
176
216
|
execution speed for programs which rely on copies (such as an undo-stack).
|
|
177
217
|
|
|
178
218
|
<!-- runkit:activate -->
|
|
219
|
+
|
|
179
220
|
```js
|
|
180
|
-
const { Map } = require('immutable')
|
|
181
|
-
const
|
|
182
|
-
const
|
|
221
|
+
const { Map } = require('immutable');
|
|
222
|
+
const map = Map({ a: 1, b: 2, c: 3 });
|
|
223
|
+
const mapCopy = map; // Look, "copies" are free!
|
|
183
224
|
```
|
|
184
225
|
|
|
185
|
-
[React]:
|
|
186
|
-
[Flux]:
|
|
226
|
+
[React]: https://reactjs.org/
|
|
227
|
+
[Flux]: https://facebook.github.io/flux/docs/in-depth-overview/
|
|
187
228
|
|
|
188
229
|
|
|
189
|
-
JavaScript-first API
|
|
190
|
-
--------------------
|
|
230
|
+
## JavaScript-first API
|
|
191
231
|
|
|
192
232
|
While Immutable.js is inspired by Clojure, Scala, Haskell and other functional
|
|
193
233
|
programming environments, it's designed to bring these powerful concepts to
|
|
194
234
|
JavaScript, and therefore has an Object-Oriented API that closely mirrors that
|
|
195
235
|
of [ES2015][] [Array][], [Map][], and [Set][].
|
|
196
236
|
|
|
197
|
-
[
|
|
198
|
-
[
|
|
199
|
-
[
|
|
200
|
-
[
|
|
237
|
+
[es2015]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla
|
|
238
|
+
[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
239
|
+
[map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
|
240
|
+
[set]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
201
241
|
|
|
202
242
|
The difference for the immutable collections is that methods which would mutate
|
|
203
243
|
the collection, like `push`, `set`, `unshift` or `splice`, instead return a new
|
|
@@ -205,9 +245,10 @@ immutable collection. Methods which return new arrays, like `slice` or `concat`,
|
|
|
205
245
|
instead return new immutable collections.
|
|
206
246
|
|
|
207
247
|
<!-- runkit:activate -->
|
|
248
|
+
|
|
208
249
|
```js
|
|
209
|
-
const { List } = require('immutable')
|
|
210
|
-
const list1 = List([
|
|
250
|
+
const { List } = require('immutable');
|
|
251
|
+
const list1 = List([1, 2]);
|
|
211
252
|
const list2 = list1.push(3, 4, 5);
|
|
212
253
|
const list3 = list2.unshift(0);
|
|
213
254
|
const list4 = list1.concat(list2, list3);
|
|
@@ -224,27 +265,34 @@ found on `Immutable.Set`, including collection operations like `forEach()`
|
|
|
224
265
|
and `map()`.
|
|
225
266
|
|
|
226
267
|
<!-- runkit:activate -->
|
|
268
|
+
|
|
227
269
|
```js
|
|
228
|
-
const { Map } = require('immutable')
|
|
270
|
+
const { Map } = require('immutable');
|
|
229
271
|
const alpha = Map({ a: 1, b: 2, c: 3, d: 4 });
|
|
230
272
|
alpha.map((v, k) => k.toUpperCase()).join();
|
|
231
273
|
// 'A,B,C,D'
|
|
232
274
|
```
|
|
233
275
|
|
|
234
|
-
###
|
|
276
|
+
### Convert from raw JavaScript objects and arrays.
|
|
235
277
|
|
|
236
278
|
Designed to inter-operate with your existing JavaScript, Immutable.js
|
|
237
|
-
accepts plain JavaScript Arrays and Objects anywhere a method expects
|
|
279
|
+
accepts plain JavaScript Arrays and Objects anywhere a method expects a
|
|
238
280
|
`Collection`.
|
|
239
281
|
|
|
240
282
|
<!-- runkit:activate -->
|
|
283
|
+
|
|
241
284
|
```js
|
|
242
|
-
const { Map } = require('immutable')
|
|
243
|
-
const map1 = Map({ a: 1, b: 2, c: 3, d: 4 })
|
|
244
|
-
const map2 = Map({ c: 10, a: 20, t: 30 })
|
|
245
|
-
const obj = { d: 100, o: 200, g: 300 }
|
|
285
|
+
const { Map, List } = require('immutable');
|
|
286
|
+
const map1 = Map({ a: 1, b: 2, c: 3, d: 4 });
|
|
287
|
+
const map2 = Map({ c: 10, a: 20, t: 30 });
|
|
288
|
+
const obj = { d: 100, o: 200, g: 300 };
|
|
246
289
|
const map3 = map1.merge(map2, obj);
|
|
247
290
|
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
|
|
291
|
+
const list1 = List([1, 2, 3]);
|
|
292
|
+
const list2 = List([4, 5, 6]);
|
|
293
|
+
const array = [7, 8, 9];
|
|
294
|
+
const list3 = list1.concat(list2, array);
|
|
295
|
+
// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
|
248
296
|
```
|
|
249
297
|
|
|
250
298
|
This is possible because Immutable.js can treat any JavaScript Array or Object
|
|
@@ -254,10 +302,13 @@ native API. Because Seq evaluates lazily and does not cache intermediate
|
|
|
254
302
|
results, these operations can be extremely efficient.
|
|
255
303
|
|
|
256
304
|
<!-- runkit:activate -->
|
|
305
|
+
|
|
257
306
|
```js
|
|
258
|
-
const { Seq } = require('immutable')
|
|
259
|
-
const myObject = { a: 1, b: 2, c: 3 }
|
|
260
|
-
Seq(myObject)
|
|
307
|
+
const { Seq } = require('immutable');
|
|
308
|
+
const myObject = { a: 1, b: 2, c: 3 };
|
|
309
|
+
Seq(myObject)
|
|
310
|
+
.map(x => x * x)
|
|
311
|
+
.toObject();
|
|
261
312
|
// { a: 1, b: 4, c: 9 }
|
|
262
313
|
```
|
|
263
314
|
|
|
@@ -266,43 +317,45 @@ JavaScript Object properties are always strings, even if written in a quote-less
|
|
|
266
317
|
shorthand, while Immutable Maps accept keys of any type.
|
|
267
318
|
|
|
268
319
|
<!-- runkit:activate -->
|
|
320
|
+
|
|
269
321
|
```js
|
|
270
|
-
const { fromJS } = require('immutable')
|
|
322
|
+
const { fromJS } = require('immutable');
|
|
271
323
|
|
|
272
|
-
const obj = { 1:
|
|
273
|
-
Object.keys(obj) // [ "1" ]
|
|
274
|
-
|
|
324
|
+
const obj = { 1: 'one' };
|
|
325
|
+
console.log(Object.keys(obj)); // [ "1" ]
|
|
326
|
+
console.log(obj['1'], obj[1]); // "one", "one"
|
|
275
327
|
|
|
276
|
-
const map = fromJS(obj)
|
|
277
|
-
|
|
328
|
+
const map = fromJS(obj);
|
|
329
|
+
console.log(map.get('1'), map.get(1)); // "one", undefined
|
|
278
330
|
```
|
|
279
331
|
|
|
280
332
|
Property access for JavaScript Objects first converts the key to a string, but
|
|
281
333
|
since Immutable Map keys can be of any type the argument to `get()` is
|
|
282
334
|
not altered.
|
|
283
335
|
|
|
284
|
-
|
|
285
336
|
### Converts back to raw JavaScript objects.
|
|
286
337
|
|
|
287
338
|
All Immutable.js Collections can be converted to plain JavaScript Arrays and
|
|
288
339
|
Objects shallowly with `toArray()` and `toObject()` or deeply with `toJS()`.
|
|
289
340
|
All Immutable Collections also implement `toJSON()` allowing them to be passed
|
|
290
|
-
to `JSON.stringify` directly.
|
|
341
|
+
to `JSON.stringify` directly. They also respect the custom `toJSON()` methods of
|
|
342
|
+
nested objects.
|
|
291
343
|
|
|
292
344
|
<!-- runkit:activate -->
|
|
345
|
+
|
|
293
346
|
```js
|
|
294
|
-
const { Map, List } = require('immutable')
|
|
295
|
-
const deep = Map({ a: 1, b: 2, c: List([
|
|
296
|
-
console.log(deep.toObject()) // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
|
|
297
|
-
console.log(deep.toArray()) // [ 1, 2, List [ 3, 4, 5 ] ]
|
|
298
|
-
console.log(deep.toJS()) // { a: 1, b: 2, c: [ 3, 4, 5 ] }
|
|
299
|
-
JSON.stringify(deep) // '{"a":1,"b":2,"c":[3,4,5]}'
|
|
347
|
+
const { Map, List } = require('immutable');
|
|
348
|
+
const deep = Map({ a: 1, b: 2, c: List([3, 4, 5]) });
|
|
349
|
+
console.log(deep.toObject()); // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
|
|
350
|
+
console.log(deep.toArray()); // [ 1, 2, List [ 3, 4, 5 ] ]
|
|
351
|
+
console.log(deep.toJS()); // { a: 1, b: 2, c: [ 3, 4, 5 ] }
|
|
352
|
+
JSON.stringify(deep); // '{"a":1,"b":2,"c":[3,4,5]}'
|
|
300
353
|
```
|
|
301
354
|
|
|
302
355
|
### Embraces ES2015
|
|
303
356
|
|
|
304
357
|
Immutable.js supports all JavaScript environments, including legacy
|
|
305
|
-
browsers (even
|
|
358
|
+
browsers (even IE11). However it also takes advantage of features added to
|
|
306
359
|
JavaScript in [ES2015][], the latest standard version of JavaScript, including
|
|
307
360
|
[Iterators][], [Arrow Functions][], [Classes][], and [Modules][]. It's inspired
|
|
308
361
|
by the native [Map][] and [Set][] collections added to ES2015.
|
|
@@ -314,25 +367,41 @@ browsers, they need to be translated to ES5.
|
|
|
314
367
|
// ES2015
|
|
315
368
|
const mapped = foo.map(x => x * x);
|
|
316
369
|
// ES5
|
|
317
|
-
var mapped = foo.map(function (x) {
|
|
370
|
+
var mapped = foo.map(function (x) {
|
|
371
|
+
return x * x;
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
All Immutable.js collections are [Iterable][iterators], which allows them to be
|
|
376
|
+
used anywhere an Iterable is expected, such as when spreading into an Array.
|
|
377
|
+
|
|
378
|
+
<!-- runkit:activate -->
|
|
379
|
+
|
|
380
|
+
```js
|
|
381
|
+
const { List } = require('immutable');
|
|
382
|
+
const aList = List([1, 2, 3]);
|
|
383
|
+
const anArray = [0, ...aList, 4, 5]; // [ 0, 1, 2, 3, 4, 5 ]
|
|
318
384
|
```
|
|
319
385
|
|
|
386
|
+
Note: A Collection is always iterated in the same order, however that order may
|
|
387
|
+
not always be well defined, as is the case for the `Map` and `Set`.
|
|
388
|
+
|
|
320
389
|
[Iterators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol
|
|
321
390
|
[Arrow Functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
|
322
|
-
[Classes]:
|
|
323
|
-
[Modules]:
|
|
391
|
+
[Classes]: https://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes
|
|
392
|
+
[Modules]: https://www.2ality.com/2014/09/es6-modules-final.html
|
|
324
393
|
|
|
325
394
|
|
|
326
|
-
Nested Structures
|
|
327
|
-
-----------------
|
|
395
|
+
## Nested Structures
|
|
328
396
|
|
|
329
397
|
The collections in Immutable.js are intended to be nested, allowing for deep
|
|
330
398
|
trees of data, similar to JSON.
|
|
331
399
|
|
|
332
400
|
<!-- runkit:activate -->
|
|
401
|
+
|
|
333
402
|
```js
|
|
334
|
-
const { fromJS } = require('immutable')
|
|
335
|
-
const nested = fromJS({ a: { b: { c: [
|
|
403
|
+
const { fromJS } = require('immutable');
|
|
404
|
+
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });
|
|
336
405
|
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }
|
|
337
406
|
```
|
|
338
407
|
|
|
@@ -341,114 +410,123 @@ most useful are `mergeDeep`, `getIn`, `setIn`, and `updateIn`, found on `List`,
|
|
|
341
410
|
`Map` and `OrderedMap`.
|
|
342
411
|
|
|
343
412
|
<!-- runkit:activate -->
|
|
413
|
+
|
|
344
414
|
```js
|
|
345
|
-
const { fromJS } = require('immutable')
|
|
346
|
-
const nested = fromJS({ a: { b: { c: [
|
|
415
|
+
const { fromJS } = require('immutable');
|
|
416
|
+
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });
|
|
347
417
|
|
|
348
|
-
const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } })
|
|
418
|
+
const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
|
|
349
419
|
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }
|
|
350
420
|
|
|
351
|
-
console.log(nested2.getIn([
|
|
421
|
+
console.log(nested2.getIn(['a', 'b', 'd'])); // 6
|
|
352
422
|
|
|
353
|
-
const nested3 = nested2.updateIn([
|
|
423
|
+
const nested3 = nested2.updateIn(['a', 'b', 'd'], value => value + 1);
|
|
354
424
|
console.log(nested3);
|
|
355
425
|
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }
|
|
356
426
|
|
|
357
|
-
const nested4 = nested3.updateIn([
|
|
427
|
+
const nested4 = nested3.updateIn(['a', 'b', 'c'], list => list.push(6));
|
|
358
428
|
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }
|
|
359
429
|
```
|
|
360
430
|
|
|
431
|
+
## Equality treats Collections as Values
|
|
361
432
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
**Seq is immutable** — Once a Seq is created, it cannot be
|
|
369
|
-
changed, appended to, rearranged or otherwise modified. Instead, any mutative
|
|
370
|
-
method called on a Seq will return a new Seq.
|
|
433
|
+
Immutable.js collections are treated as pure data _values_. Two immutable
|
|
434
|
+
collections are considered _value equal_ (via `.equals()` or `is()`) if they
|
|
435
|
+
represent the same collection of values. This differs from JavaScript's typical
|
|
436
|
+
_reference equal_ (via `===` or `==`) for Objects and Arrays which only
|
|
437
|
+
determines if two variables represent references to the same object instance.
|
|
371
438
|
|
|
372
|
-
|
|
373
|
-
|
|
439
|
+
Consider the example below where two identical `Map` instances are not
|
|
440
|
+
_reference equal_ but are _value equal_.
|
|
374
441
|
|
|
375
|
-
|
|
376
|
-
Seq is never used:
|
|
442
|
+
<!-- runkit:activate -->
|
|
377
443
|
|
|
378
444
|
```js
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
Once the Seq is used, it performs only the work necessary. In this
|
|
386
|
-
example, no intermediate arrays are ever created, filter is called three times,
|
|
387
|
-
and map is only called once:
|
|
445
|
+
// First consider:
|
|
446
|
+
const obj1 = { a: 1, b: 2, c: 3 };
|
|
447
|
+
const obj2 = { a: 1, b: 2, c: 3 };
|
|
448
|
+
obj1 !== obj2; // two different instances are always not equal with ===
|
|
388
449
|
|
|
389
|
-
|
|
390
|
-
|
|
450
|
+
const { Map, is } = require('immutable');
|
|
451
|
+
const map1 = Map({ a: 1, b: 2, c: 3 });
|
|
452
|
+
const map2 = Map({ a: 1, b: 2, c: 3 });
|
|
453
|
+
map1 !== map2; // two different instances are not reference-equal
|
|
454
|
+
map1.equals(map2); // but are value-equal if they have the same values
|
|
455
|
+
is(map1, map2); // alternatively can use the is() function
|
|
391
456
|
```
|
|
392
457
|
|
|
393
|
-
|
|
458
|
+
Value equality allows Immutable.js collections to be used as keys in Maps or
|
|
459
|
+
values in Sets, and retrieved with different but equivalent collections:
|
|
394
460
|
|
|
395
461
|
<!-- runkit:activate -->
|
|
396
|
-
```js
|
|
397
|
-
const { Map } = require('immutable')
|
|
398
|
-
const seq = Map({ a: 1, b: 2, c: 3 }).toSeq()
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
Seq allows for the efficient chaining of sequence operations, especially when
|
|
402
|
-
converting to a different concrete type (such as to a JS object):
|
|
403
462
|
|
|
404
463
|
```js
|
|
405
|
-
|
|
406
|
-
|
|
464
|
+
const { Map, Set } = require('immutable');
|
|
465
|
+
const map1 = Map({ a: 1, b: 2, c: 3 });
|
|
466
|
+
const map2 = Map({ a: 1, b: 2, c: 3 });
|
|
467
|
+
const set = Set().add(map1);
|
|
468
|
+
set.has(map2); // true because these are value-equal
|
|
407
469
|
```
|
|
408
470
|
|
|
409
|
-
|
|
471
|
+
Note: `is()` uses the same measure of equality as [Object.is][] for scalar
|
|
472
|
+
strings and numbers, but uses value equality for Immutable collections,
|
|
473
|
+
determining if both are immutable and all keys and values are equal
|
|
474
|
+
using the same measure of equality.
|
|
410
475
|
|
|
411
|
-
|
|
412
|
-
```js
|
|
413
|
-
const { Range } = require('immutable')
|
|
414
|
-
Range(1, Infinity)
|
|
415
|
-
.skip(1000)
|
|
416
|
-
.map(n => -n)
|
|
417
|
-
.filter(n => n % 2 === 0)
|
|
418
|
-
.take(2)
|
|
419
|
-
.reduce((r, n) => r * n, 1);
|
|
420
|
-
// 1006008
|
|
421
|
-
```
|
|
476
|
+
[object.is]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
|
422
477
|
|
|
423
|
-
|
|
424
|
-
|
|
478
|
+
#### Performance tradeoffs
|
|
479
|
+
|
|
480
|
+
While value equality is useful in many circumstances, it has different
|
|
481
|
+
performance characteristics than reference equality. Understanding these
|
|
482
|
+
tradeoffs may help you decide which to use in each case, especially when used
|
|
483
|
+
to memoize some operation.
|
|
425
484
|
|
|
485
|
+
When comparing two collections, value equality may require considering every
|
|
486
|
+
item in each collection, on an `O(N)` time complexity. For large collections of
|
|
487
|
+
values, this could become a costly operation. Though if the two are not equal
|
|
488
|
+
and hardly similar, the inequality is determined very quickly. In contrast, when
|
|
489
|
+
comparing two collections with reference equality, only the initial references
|
|
490
|
+
to memory need to be compared which is not based on the size of the collections,
|
|
491
|
+
which has an `O(1)` time complexity. Checking reference equality is always very
|
|
492
|
+
fast, however just because two collections are not reference-equal does not rule
|
|
493
|
+
out the possibility that they may be value-equal.
|
|
426
494
|
|
|
427
|
-
|
|
428
|
-
-----------------------------------
|
|
495
|
+
#### Return self on no-op optimization
|
|
429
496
|
|
|
430
|
-
Immutable.js
|
|
431
|
-
|
|
497
|
+
When possible, Immutable.js avoids creating new objects for updates where no
|
|
498
|
+
change in _value_ occurred, to allow for efficient _reference equality_ checking
|
|
499
|
+
to quickly determine if no change occurred.
|
|
432
500
|
|
|
433
501
|
<!-- runkit:activate -->
|
|
502
|
+
|
|
434
503
|
```js
|
|
435
|
-
const { Map
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
assert.equal(is(map1, map2), true) // have equivalent values
|
|
440
|
-
assert.equal(map1.equals(map2), true) // alternatively use the equals method
|
|
504
|
+
const { Map } = require('immutable');
|
|
505
|
+
const originalMap = Map({ a: 1, b: 2, c: 3 });
|
|
506
|
+
const updatedMap = originalMap.set('b', 2);
|
|
507
|
+
updatedMap === originalMap; // No-op .set() returned the original reference.
|
|
441
508
|
```
|
|
442
509
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
510
|
+
However updates which do result in a change will return a new reference. Each
|
|
511
|
+
of these operations occur independently, so two similar updates will not return
|
|
512
|
+
the same reference:
|
|
446
513
|
|
|
447
|
-
|
|
514
|
+
<!-- runkit:activate -->
|
|
448
515
|
|
|
516
|
+
```js
|
|
517
|
+
const { Map } = require('immutable');
|
|
518
|
+
const originalMap = Map({ a: 1, b: 2, c: 3 });
|
|
519
|
+
const updatedMap = originalMap.set('b', 1000);
|
|
520
|
+
// New instance, leaving the original immutable.
|
|
521
|
+
updatedMap !== originalMap;
|
|
522
|
+
const anotherUpdatedMap = originalMap.set('b', 1000);
|
|
523
|
+
// Despite both the results of the same operation, each created a new reference.
|
|
524
|
+
anotherUpdatedMap !== updatedMap;
|
|
525
|
+
// However the two are value equal.
|
|
526
|
+
anotherUpdatedMap.equals(updatedMap);
|
|
527
|
+
```
|
|
449
528
|
|
|
450
|
-
Batching Mutations
|
|
451
|
-
------------------
|
|
529
|
+
## Batching Mutations
|
|
452
530
|
|
|
453
531
|
> If a tree falls in the woods, does it make a sound?
|
|
454
532
|
>
|
|
@@ -462,15 +540,16 @@ which can add up to a minor performance penalty. If you need to apply a series
|
|
|
462
540
|
of mutations locally before returning, Immutable.js gives you the ability to
|
|
463
541
|
create a temporary mutable (transient) copy of a collection and apply a batch of
|
|
464
542
|
mutations in a performant manner by using `withMutations`. In fact, this is
|
|
465
|
-
exactly how
|
|
543
|
+
exactly how Immutable.js applies complex mutations itself.
|
|
466
544
|
|
|
467
545
|
As an example, building `list2` results in the creation of 1, not 3, new
|
|
468
546
|
immutable Lists.
|
|
469
547
|
|
|
470
548
|
<!-- runkit:activate -->
|
|
549
|
+
|
|
471
550
|
```js
|
|
472
|
-
const { List } = require('immutable')
|
|
473
|
-
const list1 = List([
|
|
551
|
+
const { List } = require('immutable');
|
|
552
|
+
const list1 = List([1, 2, 3]);
|
|
474
553
|
const list2 = list1.withMutations(function (list) {
|
|
475
554
|
list.push(4).push(5).push(6);
|
|
476
555
|
});
|
|
@@ -482,56 +561,157 @@ Note: Immutable.js also provides `asMutable` and `asImmutable`, but only
|
|
|
482
561
|
encourages their use when `withMutations` will not suffice. Use caution to not
|
|
483
562
|
return a mutable copy, which could result in undesired behavior.
|
|
484
563
|
|
|
485
|
-
|
|
564
|
+
_Important!_: Only a select few methods can be used in `withMutations` including
|
|
486
565
|
`set`, `push` and `pop`. These methods can be applied directly against a
|
|
487
566
|
persistent data-structure where other methods like `map`, `filter`, `sort`,
|
|
488
567
|
and `splice` will always return new immutable data-structures and never mutate
|
|
489
568
|
a mutable collection.
|
|
490
569
|
|
|
570
|
+
## Lazy Seq
|
|
491
571
|
|
|
492
|
-
|
|
493
|
-
|
|
572
|
+
`Seq` describes a lazy operation, allowing them to efficiently chain
|
|
573
|
+
use of all the higher-order collection methods (such as `map` and `filter`)
|
|
574
|
+
by not creating intermediate collections.
|
|
494
575
|
|
|
495
|
-
|
|
576
|
+
**Seq is immutable** — Once a Seq is created, it cannot be
|
|
577
|
+
changed, appended to, rearranged or otherwise modified. Instead, any mutative
|
|
578
|
+
method called on a `Seq` will return a new `Seq`.
|
|
496
579
|
|
|
497
|
-
|
|
498
|
-
|
|
580
|
+
**Seq is lazy** — `Seq` does as little work as necessary to respond to any
|
|
581
|
+
method call. Values are often created during iteration, including implicit
|
|
582
|
+
iteration when reducing or converting to a concrete data structure such as
|
|
583
|
+
a `List` or JavaScript `Array`.
|
|
499
584
|
|
|
500
|
-
|
|
501
|
-
|
|
585
|
+
For example, the following performs no work, because the resulting
|
|
586
|
+
`Seq`'s values are never iterated:
|
|
502
587
|
|
|
588
|
+
```js
|
|
589
|
+
const { Seq } = require('immutable');
|
|
590
|
+
const oddSquares = Seq([1, 2, 3, 4, 5, 6, 7, 8])
|
|
591
|
+
.filter(x => x % 2 !== 0)
|
|
592
|
+
.map(x => x * x);
|
|
593
|
+
```
|
|
503
594
|
|
|
504
|
-
|
|
505
|
-
|
|
595
|
+
Once the `Seq` is used, it performs only the work necessary. In this
|
|
596
|
+
example, no intermediate arrays are ever created, filter is called three
|
|
597
|
+
times, and map is only called once:
|
|
506
598
|
|
|
507
|
-
|
|
599
|
+
```js
|
|
600
|
+
oddSquares.get(1); // 9
|
|
601
|
+
```
|
|
508
602
|
|
|
603
|
+
Any collection can be converted to a lazy Seq with `Seq()`.
|
|
509
604
|
|
|
510
|
-
|
|
511
|
-
------------
|
|
605
|
+
<!-- runkit:activate -->
|
|
512
606
|
|
|
513
|
-
|
|
607
|
+
```js
|
|
608
|
+
const { Map, Seq } = require('immutable');
|
|
609
|
+
const map = Map({ a: 1, b: 2, c: 3 });
|
|
610
|
+
const lazySeq = Seq(map);
|
|
611
|
+
```
|
|
514
612
|
|
|
515
|
-
|
|
613
|
+
`Seq` allows for the efficient chaining of operations, allowing for the
|
|
614
|
+
expression of logic that can otherwise be very tedious:
|
|
516
615
|
|
|
616
|
+
```js
|
|
617
|
+
lazySeq
|
|
618
|
+
.flip()
|
|
619
|
+
.map(key => key.toUpperCase())
|
|
620
|
+
.flip();
|
|
621
|
+
// Seq { A: 1, B: 2, C: 3 }
|
|
622
|
+
```
|
|
517
623
|
|
|
518
|
-
|
|
519
|
-
|
|
624
|
+
As well as expressing logic that would otherwise seem memory or time
|
|
625
|
+
limited, for example `Range` is a special kind of Lazy sequence.
|
|
520
626
|
|
|
521
|
-
|
|
627
|
+
<!-- runkit:activate -->
|
|
522
628
|
|
|
629
|
+
```js
|
|
630
|
+
const { Range } = require('immutable');
|
|
631
|
+
Range(1, Infinity)
|
|
632
|
+
.skip(1000)
|
|
633
|
+
.map(n => -n)
|
|
634
|
+
.filter(n => n % 2 === 0)
|
|
635
|
+
.take(2)
|
|
636
|
+
.reduce((r, n) => r * n, 1);
|
|
637
|
+
// 1006008
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Additional Tools and Resources
|
|
641
|
+
|
|
642
|
+
- [Atom-store](https://github.com/jameshopkins/atom-store/)
|
|
643
|
+
- A Clojure-inspired atom implementation in Javascript with configurability
|
|
644
|
+
for external persistance.
|
|
645
|
+
|
|
646
|
+
- [Chai Immutable](https://github.com/astorije/chai-immutable)
|
|
647
|
+
- If you are using the [Chai Assertion Library](https://chaijs.com/), this
|
|
648
|
+
provides a set of assertions to use against Immutable.js collections.
|
|
649
|
+
|
|
650
|
+
- [Fantasy-land](https://github.com/fantasyland/fantasy-land)
|
|
651
|
+
- Specification for interoperability of common algebraic structures in JavaScript.
|
|
652
|
+
|
|
653
|
+
- [Immutagen](https://github.com/pelotom/immutagen)
|
|
654
|
+
- A library for simulating immutable generators in JavaScript.
|
|
655
|
+
|
|
656
|
+
- [Immutable-cursor](https://github.com/redbadger/immutable-cursor)
|
|
657
|
+
- Immutable cursors incorporating the Immutable.js interface over
|
|
658
|
+
Clojure-inspired atom.
|
|
659
|
+
|
|
660
|
+
- [Immutable-ext](https://github.com/DrBoolean/immutable-ext)
|
|
661
|
+
- Fantasyland extensions for immutablejs
|
|
662
|
+
|
|
663
|
+
- [Immutable-js-tools](https://github.com/madeinfree/immutable-js-tools)
|
|
664
|
+
- Util tools for immutable.js
|
|
665
|
+
|
|
666
|
+
- [Immutable-Redux](https://github.com/gajus/redux-immutable)
|
|
667
|
+
- redux-immutable is used to create an equivalent function of Redux
|
|
668
|
+
combineReducers that works with Immutable.js state.
|
|
669
|
+
|
|
670
|
+
- [Immutable-Treeutils](https://github.com/lukasbuenger/immutable-treeutils)
|
|
671
|
+
- Functional tree traversal helpers for ImmutableJS data structures.
|
|
672
|
+
|
|
673
|
+
- [Irecord](https://github.com/ericelliott/irecord)
|
|
674
|
+
- An immutable store that exposes an RxJS observable. Great for React.
|
|
675
|
+
|
|
676
|
+
- [Mudash](https://github.com/brianneisler/mudash)
|
|
677
|
+
- Lodash wrapper providing Immutable.JS support.
|
|
523
678
|
|
|
524
|
-
|
|
525
|
-
|
|
679
|
+
- [React-Immutable-PropTypes](https://github.com/HurricaneJames/react-immutable-proptypes)
|
|
680
|
+
- PropType validators that work with Immutable.js.
|
|
681
|
+
|
|
682
|
+
- [Redux-Immutablejs](https://github.com/indexiatech/redux-immutablejs)
|
|
683
|
+
- Redux Immutable facilities.
|
|
684
|
+
|
|
685
|
+
- [Rxstate](https://github.com/yamalight/rxstate)
|
|
686
|
+
- Simple opinionated state management library based on RxJS and Immutable.js.
|
|
687
|
+
|
|
688
|
+
- [Transit-Immutable-js](https://github.com/glenjamin/transit-immutable-js)
|
|
689
|
+
- Transit serialisation for Immutable.js.
|
|
690
|
+
- See also: [Transit-js](https://github.com/cognitect/transit-js)
|
|
691
|
+
|
|
692
|
+
Have an additional tool designed to work with Immutable.js?
|
|
693
|
+
Submit a PR to add it to this list in alphabetical order.
|
|
694
|
+
|
|
695
|
+
## Contributing
|
|
696
|
+
|
|
697
|
+
Use [Github issues](https://github.com/immutable-js/immutable-js/issues) for requests.
|
|
698
|
+
|
|
699
|
+
We actively welcome pull requests, learn how to [contribute](https://github.com/immutable-js/immutable-js/blob/main/.github/CONTRIBUTING.md).
|
|
700
|
+
|
|
701
|
+
Immutable.js is maintained within the [Contributor Covenant's Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/).
|
|
702
|
+
|
|
703
|
+
### Changelog
|
|
704
|
+
|
|
705
|
+
Changes are tracked as [Github releases](https://github.com/immutable-js/immutable-js/releases).
|
|
706
|
+
|
|
707
|
+
### License
|
|
708
|
+
|
|
709
|
+
Immutable.js is [MIT-licensed](./LICENSE).
|
|
710
|
+
|
|
711
|
+
### Thanks
|
|
526
712
|
|
|
527
713
|
[Phil Bagwell](https://www.youtube.com/watch?v=K2NYwP90bNs), for his inspiration
|
|
528
714
|
and research in persistent data structures.
|
|
529
715
|
|
|
530
716
|
[Hugh Jackson](https://github.com/hughfdjackson/), for providing the npm package
|
|
531
717
|
name. If you're looking for his unsupported package, see [this repository](https://github.com/hughfdjackson/immutable).
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
License
|
|
535
|
-
-------
|
|
536
|
-
|
|
537
|
-
Immutable.js is [MIT-licensed](https://github.com/facebook/immutable-js/blob/master/LICENSE).
|