immutable 4.0.0-rc.5 → 4.0.0-rc.9

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/README.md CHANGED
@@ -51,44 +51,48 @@ map1.get('b') + " vs. " + map2.get('b') // 2 vs. 50
51
51
 
52
52
  ### Browser
53
53
 
54
- To use Immutable.js from a browser, download [dist/immutable.min.js](https://github.com/facebook/immutable-js/blob/master/dist/immutable.min.js)
55
- or use a CDN such as [CDNJS](https://cdnjs.com/libraries/immutable)
56
- or [jsDelivr](http://www.jsdelivr.com/#!immutable.js).
54
+ Immutable.js has no dependencies, which makes it predictable to include in a Browser.
57
55
 
58
- Then, add it as a script tag to your page:
56
+ It's highly recommended to use a module bundler like [webpack](https://webpack.github.io/),
57
+ [rollup](https://rollupjs.org/), or
58
+ [browserify](http://browserify.org/). The `immutable` npm module works
59
+ without any additional consideration. All examples throughout the documentation
60
+ will assume use of this kind of tool.
61
+
62
+ Alternatively, Immutable.js may be directly included as a script tag. Download
63
+ or link to a CDN such as [CDNJS](https://cdnjs.com/libraries/immutable)
64
+ or [jsDelivr](https://www.jsdelivr.com/package/npm/immutable).
65
+
66
+ Use a script tag to directly add `Immutable` to the global scope:
59
67
 
60
68
  ```html
61
69
  <script src="immutable.min.js"></script>
62
70
  <script>
63
- var map1 = Immutable.Map({a:1, b:2, c:3});
64
- var map2 = map1.set('b', 50);
65
- map1.get('b'); // 2
66
- map2.get('b'); // 50
71
+ var map1 = Immutable.Map({a:1, b:2, c:3});
72
+ var map2 = map1.set('b', 50);
73
+ map1.get('b'); // 2
74
+ map2.get('b'); // 50
67
75
  </script>
68
76
  ```
69
77
 
70
- Or use an AMD loader (such as [RequireJS](http://requirejs.org/)):
78
+ Or use an AMD-style loader (such as [RequireJS](http://requirejs.org/)):
71
79
 
72
80
  ```js
73
81
  require(['./immutable.min.js'], function (Immutable) {
74
- var map1 = Immutable.Map({a:1, b:2, c:3});
75
- var map2 = map1.set('b', 50);
76
- map1.get('b'); // 2
77
- map2.get('b'); // 50
82
+ var map1 = Immutable.Map({a:1, b:2, c:3});
83
+ var map2 = map1.set('b', 50);
84
+ map1.get('b'); // 2
85
+ map2.get('b'); // 50
78
86
  });
79
87
  ```
80
88
 
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
89
  ### Flow & TypeScript
86
90
 
87
91
  Use these Immutable collections and sequences as you would use native
88
92
  collections in your [Flowtype](https://flowtype.org/) or [TypeScript](http://typescriptlang.org) programs while still taking
89
93
  advantage of type generics, error detection, and auto-complete in your IDE.
90
94
 
91
- Installing `immutable` via npm brings with it type definitions for Flow (v0.39.0 or higher)
95
+ Installing `immutable` via npm brings with it type definitions for Flow (v0.55.0 or higher)
92
96
  and TypeScript (v2.1.0 or higher), so you shouldn't need to do anything at all!
93
97
 
94
98
  #### Using TypeScript with Immutable.js v4
@@ -231,7 +235,7 @@ alpha.map((v, k) => k.toUpperCase()).join();
231
235
  // 'A,B,C,D'
232
236
  ```
233
237
 
234
- ### Accepts raw JavaScript objects.
238
+ ### Convert from raw JavaScript objects and arrays.
235
239
 
236
240
  Designed to inter-operate with your existing JavaScript, Immutable.js
237
241
  accepts plain JavaScript Arrays and Objects anywhere a method expects an
@@ -239,12 +243,17 @@ accepts plain JavaScript Arrays and Objects anywhere a method expects an
239
243
 
240
244
  <!-- runkit:activate -->
241
245
  ```js
242
- const { Map } = require('immutable')
246
+ const { Map, List } = require('immutable')
243
247
  const map1 = Map({ a: 1, b: 2, c: 3, d: 4 })
244
248
  const map2 = Map({ c: 10, a: 20, t: 30 })
245
249
  const obj = { d: 100, o: 200, g: 300 }
246
250
  const map3 = map1.merge(map2, obj);
247
251
  // Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
252
+ const list1 = List([ 1, 2, 3 ])
253
+ const list2 = List([ 4, 5, 6 ])
254
+ const array = [ 7, 8, 9 ]
255
+ const list3 = list1.concat(list2, array)
256
+ // List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
248
257
  ```
249
258
 
250
259
  This is possible because Immutable.js can treat any JavaScript Array or Object
@@ -317,6 +326,19 @@ const mapped = foo.map(x => x * x);
317
326
  var mapped = foo.map(function (x) { return x * x; });
318
327
  ```
319
328
 
329
+ All Immutable.js collections are [Iterable][Iterators], which allows them to be
330
+ used anywhere an Iterable is expected, such as when spreading into an Array.
331
+
332
+ <!-- runkit:activate -->
333
+ ```js
334
+ const { List } = require('immutable')
335
+ const aList = List([ 1, 2, 3 ])
336
+ const anArray = [ 0, ...aList, 4, 5 ] // [ 0, 1, 2, 3, 4, 5 ]
337
+ ```
338
+
339
+ Note: A Collection is always iterated in the same order, however that order may
340
+ not always be well defined, as is the case for the `Map` and `Set`.
341
+
320
342
  [Iterators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol
321
343
  [Arrow Functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
322
344
  [Classes]: http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes
@@ -359,93 +381,100 @@ const nested4 = nested3.updateIn([ 'a', 'b', 'c' ], list => list.push(6))
359
381
  ```
360
382
 
361
383
 
362
- Lazy Seq
363
- --------
364
-
365
- `Seq` describes a lazy operation, allowing them to efficiently chain
366
- use of all the sequence methods (such as `map` and `filter`).
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.
384
+ Equality treats Collections as Values
385
+ -------------------------------------
371
386
 
372
- **Seq is lazy** Seq does as little work as necessary to respond to any
373
- method call.
387
+ Immutable.js collections are treated as pure data *values*. Two immutable
388
+ collections are considered *value equal* (via `.equals()` or `is()`) if they
389
+ represent the same collection of values. This differs from JavaScript's typical
390
+ *reference equal* (via `===` or `==`) for Objects and Arrays which only
391
+ determines if two variables represent references to the same object instance.
374
392
 
375
- For example, the following does not perform any work, because the resulting
376
- Seq is never used:
393
+ Consider the example below where two identical `Map` instances are not
394
+ *reference equal* but are *value equal*.
377
395
 
396
+ <!-- runkit:activate -->
378
397
  ```js
379
- const { Seq } = require('immutable')
380
- const oddSquares = Seq([ 1, 2, 3, 4, 5, 6, 7, 8 ])
381
- .filter(x => x % 2)
382
- .map(x => x * x)
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:
398
+ // First consider:
399
+ const obj1 = { a: 1, b: 2, c: 3 }
400
+ const obj2 = { a: 1, b: 2, c: 3 }
401
+ obj1 !== obj2 // two different instances are always not equal with ===
388
402
 
389
- ```js
390
- console.log(oddSquares.get(1)); // 9
403
+ const { Map, is } = require('immutable')
404
+ const map1 = Map({ a: 1, b: 2, c: 3 })
405
+ const map2 = Map({ a: 1, b: 2, c: 3 })
406
+ map1 !== map2 // two different instances are not reference-equal
407
+ map1.equals(map2) // but are value-equal if they have the same values
408
+ is(map1, map2) // alternatively can use the is() function
391
409
  ```
392
410
 
393
- Any collection can be converted to a lazy Seq with `.toSeq()`.
411
+ Value equality allows Immutable.js collections to be used as keys in Maps or
412
+ values in Sets, and retrieved with different but equivalent collections:
394
413
 
395
414
  <!-- runkit:activate -->
396
415
  ```js
397
- const { Map } = require('immutable')
398
- const seq = Map({ a: 1, b: 2, c: 3 }).toSeq()
416
+ const { Map, Set } = require('immutable')
417
+ const map1 = Map({ a: 1, b: 2, c: 3 })
418
+ const map2 = Map({ a: 1, b: 2, c: 3 })
419
+ const set = Set().add(map1)
420
+ set.has(map2) // true because these are value-equal
399
421
  ```
400
422
 
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
-
404
- ```js
405
- seq.flip().map(key => key.toUpperCase()).flip().toObject();
406
- // { A: 1, B: 2, C: 3 }
407
- ```
423
+ Note: `is()` uses the same measure of equality as [Object.is][] for scalar
424
+ strings and numbers, but uses value equality for Immutable collections,
425
+ determining if both are immutable and all keys and values are equal
426
+ using the same measure of equality.
408
427
 
409
- As well as expressing logic that would otherwise seem memory-limited:
428
+ [Object.is]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
410
429
 
411
- <!-- runkit:activate -->
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
- ```
430
+ #### Performance tradeoffs
422
431
 
423
- Note: A Collection is always iterated in the same order, however that order may
424
- not always be well defined, as is the case for the `Map`.
432
+ While value equality is useful in many circumstances, it has different
433
+ performance characteristics than reference equality. Understanding these
434
+ tradeoffs may help you decide which to use in each case, especially when used
435
+ to memoize some operation.
425
436
 
437
+ When comparing two collections, value equality may require considering every
438
+ item in each collection, on an `O(N)` time complexity. For large collections of
439
+ values, this could become a costly operation. Though if the two are not equal
440
+ and hardly similar, the inequality is determined very quickly. In contrast, when
441
+ comparing two collections with reference equality, only the initial references
442
+ to memory need to be compared which is not based on the size of the collections,
443
+ which has an `O(1)` time complexity. Checking reference equality is always very
444
+ fast, however just because two collections are not reference-equal does not rule
445
+ out the possibility that they may be value-equal.
426
446
 
427
- Equality treats Collections as Data
428
- -----------------------------------
447
+ #### Return self on no-op optimization
429
448
 
430
- Immutable.js provides equality which treats immutable data structures as pure
431
- data, performing a deep equality check if necessary.
449
+ When possible, Immutable.js avoids creating new objects for updates where no
450
+ change in *value* occurred, to allow for efficient *reference equality* checking
451
+ to quickly determine if no change occurred.
432
452
 
433
453
  <!-- runkit:activate -->
434
454
  ```js
435
- const { Map, is } = require('immutable')
436
- const map1 = Map({ a: 1, b: 2, c: 3 })
437
- const map2 = Map({ a: 1, b: 2, c: 3 })
438
- assert.equal(map1 !== map2, true) // two different instances
439
- assert.equal(is(map1, map2), true) // have equivalent values
440
- assert.equal(map1.equals(map2), true) // alternatively use the equals method
455
+ const { Map } = require('immutable')
456
+ const originalMap = Map({ a: 1, b: 2, c: 3 })
457
+ const updatedMap = originalMap.set('b', 2)
458
+ updatedMap === originalMap // No-op .set() returned the original reference.
441
459
  ```
442
460
 
443
- `Immutable.is()` uses the same measure of equality as [Object.is][]
444
- including if both are immutable and all keys and values are equal
445
- using the same measure of equality.
446
-
447
- [Object.is]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
461
+ However updates which do result in a change will return a new reference. Each
462
+ of these operations occur independently, so two similar updates will not return
463
+ the same reference:
448
464
 
465
+ <!-- runkit:activate -->
466
+ ```js
467
+ const { Map } = require('immutable')
468
+ const originalMap = Map({ a: 1, b: 2, c: 3 })
469
+ const updatedMap = originalMap.set('b', 1000)
470
+ // New instance, leaving the original immutable.
471
+ updatedMap !== originalMap
472
+ const anotherUpdatedMap = originalMap.set('b', 1000)
473
+ // Despite both the results of the same operation, each created a new reference.
474
+ anotherUpdatedMap !== updatedMap
475
+ // However the two are value equal.
476
+ anotherUpdatedMap.equals(updatedMap)
477
+ ```
449
478
 
450
479
  Batching Mutations
451
480
  ------------------
@@ -489,6 +518,76 @@ and `splice` will always return new immutable data-structures and never mutate
489
518
  a mutable collection.
490
519
 
491
520
 
521
+ Lazy Seq
522
+ --------
523
+
524
+ `Seq` describes a lazy operation, allowing them to efficiently chain
525
+ use of all the higher-order collection methods (such as `map` and `filter`)
526
+ by not creating intermediate collections.
527
+
528
+ **Seq is immutable** — Once a Seq is created, it cannot be
529
+ changed, appended to, rearranged or otherwise modified. Instead, any mutative
530
+ method called on a `Seq` will return a new `Seq`.
531
+
532
+ **Seq is lazy** — `Seq` does as little work as necessary to respond to any
533
+ method call. Values are often created during iteration, including implicit
534
+ iteration when reducing or converting to a concrete data structure such as
535
+ a `List` or JavaScript `Array`.
536
+
537
+ For example, the following performs no work, because the resulting
538
+ `Seq`'s values are never iterated:
539
+
540
+ ```js
541
+ const { Seq } = require('immutable')
542
+ const oddSquares = Seq([ 1, 2, 3, 4, 5, 6, 7, 8 ])
543
+ .filter(x => x % 2 !== 0)
544
+ .map(x => x * x)
545
+ ```
546
+
547
+ Once the `Seq` is used, it performs only the work necessary. In this
548
+ example, no intermediate arrays are ever created, filter is called three
549
+ times, and map is only called once:
550
+
551
+ ```js
552
+ oddSquares.get(1); // 9
553
+ ```
554
+
555
+ Any collection can be converted to a lazy Seq with `Seq()`.
556
+
557
+ <!-- runkit:activate -->
558
+ ```js
559
+ const { Map } = require('immutable')
560
+ const map = Map({ a: 1, b: 2, c: 3 }
561
+ const lazySeq = Seq(map)
562
+ ```
563
+
564
+ `Seq` allows for the efficient chaining of operations, allowing for the
565
+ expression of logic that can otherwise be very tedious:
566
+
567
+ ```js
568
+ lazySeq
569
+ .flip()
570
+ .map(key => key.toUpperCase())
571
+ .flip()
572
+ // Seq { A: 1, B: 1, C: 1 }
573
+ ```
574
+
575
+ As well as expressing logic that would otherwise seem memory or time
576
+ limited, for example `Range` is a special kind of Lazy sequence.
577
+
578
+ <!-- runkit:activate -->
579
+ ```js
580
+ const { Range } = require('immutable')
581
+ Range(1, Infinity)
582
+ .skip(1000)
583
+ .map(n => -n)
584
+ .filter(n => n % 2 === 0)
585
+ .take(2)
586
+ .reduce((r, n) => r * n, 1)
587
+ // 1006008
588
+ ```
589
+
590
+
492
591
  Documentation
493
592
  -------------
494
593