├── 10 ├── 01-not-the-same-as-lists.js ├── 02-lists-to-sets-and-back.js ├── 03-lazy-duplicate-removal.js ├── 04-sorting-ordered-sets.js ├── 05-iterating-ordered-sets.js └── 06-adding-duplicates-to-sets.js ├── 11 ├── 01-set-intersections.js ├── 02-list-intersections.js ├── 03-set-differences.js ├── 04-list-differences.js ├── 05-map-key-differences.js └── 06-list-subsets.js ├── 12 ├── 01-merging-maps-by-key.js ├── 02-merging-maps-with-complex-keys.js ├── 03-merging-lists-of-simple-values.js ├── 04-merging-lists-of-maps.js ├── 05-merging-lists-of-lists.js ├── 06-concatenating-list-values.js ├── 07-lazy-concatenating-sequences.js ├── 08-interposing-values.js └── 09-interleaving-values.js ├── 13 ├── 01-maps-of-behavior.js ├── 02-wrapping-behavior-maps-in-functions.js ├── 03-providing-default-behavior.js ├── 04-parameterizing-behavior.js ├── 05-composing-behavior.js └── 06-declarative-and-or-conditions.js ├── 14 ├── immutable-dom.html ├── immutable-dom.js ├── immutable-react.html ├── immutable-react.js ├── immutable-react.jsx └── styles.css ├── 15 ├── 01-reading-lines-of-data-into-collections.js ├── 02-reading-words-concat.js ├── 03-reading-words-push.js ├── 04-reading-words-with-mutations.js ├── 05-writing-collection-data.js ├── 06-writing-sequence-data.js ├── 07-chaining-lazy-io-streams.js ├── input │ ├── 01-data.csv │ └── words └── output │ ├── 05-writing-collection-data │ └── 06-writing-sequence-data ├── 16 ├── immutable-app.js ├── immutable-dom.html ├── immutable-dom.js └── styles.css ├── .babelrc ├── .eslintrc ├── .gitignore ├── 02 ├── 01-available-types.js ├── 02-passing-javascript-data.js ├── 03-passing-immutable-data.js ├── 04-the-of-method.js └── 05-the-fromjs-function.js ├── 03 ├── 01-new-list-items.js ├── 02-new-map-key-value-pairs.js ├── 03-pushing-multiple-list-values.js ├── 04-setting-multiple-map-values.js ├── 05-changing-list-items.js ├── 06-updating-list-values.js ├── 07-changing-map-values.js ├── 08-updating-map-values.js ├── 09-chaining-collection-mutation-methods.js ├── 10-deleting-list-items.js ├── 11-deleting-map-key-value-pairs.js ├── 12-chaining-removal-methods.js ├── 13-deleting-by-replacing-collections.js ├── 14-deleting-by-clearing-collections.js └── 15-mutation-history-stacks.js ├── 04 ├── 01-filtering-by-value.js ├── 02-filtering-by-greater-lesser.js ├── 03-filtering-by-negation.js ├── 04-filtering-maps-by-keys.js ├── 05-filtering-by-fancy-keys.js ├── 06-existence-checks.js ├── 07-finding-items.js ├── 08-the-is-function.js ├── 09-searching-lists-of-maps.js ├── 10-partial-map-search-matches.js └── 11-search-direction.js ├── 05 ├── 01-basic-sequence-creation.js ├── 02-collections-to-sequences.js ├── 03-for-loop-iteration.js ├── 04-the-for-each-iterator.js ├── 05-sequence-filters.js ├── 06-multiple-sequence-filters.js ├── 07-removing-work-with-take.js └── 08-pagination-using-slice.js ├── 06 ├── 01-deafult-sorting.js ├── 02-reversed-sorting.js ├── 03-sorting-lists-of-maps.js ├── 04-ordering-maps.js ├── 05-sorting-maps.js └── 06-maintaining-sort-order.js ├── 07 ├── 01-plucking-property-values.js ├── 02-computing-new-values.js ├── 03-mapping-new-properties.js ├── 04-mapping-specific-properties.js ├── 05-reducing-max-min.js ├── 06-accumulating-values.js ├── 07-multiple-map-calls.js ├── 08-filtering-before-mapping.js └── 09-filter-map-reduce.js ├── 08 ├── 01-zipping-lists-of-simple-values.js ├── 02-zipping-lists-of-maps.js ├── 03-lazy-zipping.js ├── 04-deep-flattening-and-filtering.js ├── 05-shallow-flattening.js └── 06-flattening-nested-maps.js ├── 09 ├── 01-strict-equality.js ├── 02-strict-equality-vs-equals.js ├── 03-transformations-versus-mutations.js ├── 04-detecting-after-transfomations.js ├── 05-detecting-before-transfomations.js └── 06-caching-function-arguments.js ├── LICENSE ├── README.md ├── package-lock.json └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-es2015-modules-commonjs"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "node": true, 5 | "browser": true 6 | }, 7 | "parserOptions": { 8 | "sourceType": "module", 9 | "ecmaVersion": 8 10 | }, 11 | "rules": { 12 | "comma-dangle": ["error", "never"], 13 | "no-console": 0, 14 | "no-confusing-arrow": 0, 15 | "no-restricted-syntax": 0, 16 | "no-bitwise": 0, 17 | "no-nested-ternary": 0, 18 | "no-await-in-loop": 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /02/01-available-types.js: -------------------------------------------------------------------------------- 1 | import { 2 | List, 3 | Map, 4 | OrderedMap, 5 | Set, 6 | OrderedSet, 7 | Seq, 8 | Stack, 9 | Record 10 | } from 'immutable'; 11 | 12 | // Returns new instances of immutable data types. 13 | const myList = List(); 14 | const myMap = Map(); 15 | const myOrderedMap = OrderedMap(); 16 | const mySet = Set(); 17 | const myOrderedSet = OrderedSet(); 18 | const mySeq = Seq(); 19 | const myStack = Stack(); 20 | 21 | // The Record() function returns a new class, 22 | // so we have to instantiate this ourselves. 23 | const MyRecord = Record({}); 24 | const myRecord = new MyRecord(); 25 | 26 | console.log('List', myList instanceof List); 27 | // -> List true 28 | console.log('Map', myMap instanceof Map); 29 | // -> Map true 30 | console.log('OrderedMap', myOrderedMap instanceof OrderedMap); 31 | // -> OrderedMap true 32 | console.log('Set', mySet instanceof Set); 33 | // -> Set true 34 | console.log('OrderedSet', myOrderedSet instanceof OrderedSet); 35 | // -> OrderedSet true 36 | console.log('Seq', mySeq instanceof Seq); 37 | // -> Seq true 38 | console.log('Stack', myStack instanceof Stack); 39 | // -> Stack true 40 | console.log('Record', myRecord instanceof Record); 41 | // -> Record true 42 | -------------------------------------------------------------------------------- /02/02-passing-javascript-data.js: -------------------------------------------------------------------------------- 1 | import { 2 | List, 3 | Map 4 | } from 'immutable'; 5 | 6 | // Lists can take Javascript arrays while Maps can 7 | // take Javascript objects. 8 | const myList = List([1, 2, 3]); 9 | const myMap = Map({ a: 1, b: 2, c: 3 }); 10 | 11 | // Lists and Maps have a get() method. 12 | console.log('myList', myList.get(1)); 13 | // -> myList 2 14 | console.log('myMap', myMap.get('b')); 15 | // -> myMap 2 16 | -------------------------------------------------------------------------------- /02/03-passing-immutable-data.js: -------------------------------------------------------------------------------- 1 | import { 2 | List, 3 | Map 4 | } from 'immutable'; 5 | 6 | // Passing other Immutable.js types works fine. 7 | const myList = List(List([1, 2, 3])); 8 | const firstMap = Map({ a: 1, b: 2, c: 3 }); 9 | const myMap = Map(firstMap); 10 | 11 | // We get List and Map instances as expected. 12 | console.log('myList', myList.get(0)); 13 | // -> myList 1 14 | console.log('myMap', myMap.get('a')); 15 | // -> myMap 1 16 | 17 | // But the same instance is reused. Why not? It can't 18 | // change! 19 | console.log('firstMap === myMap', firstMap === myMap); 20 | // -> firstMap === myMap true 21 | 22 | const myFunc = map => Map(map).toJS(); 23 | 24 | console.log('myFunc(object)', myFunc({ a: 1, b: 2, c: 3 })); 25 | // -> myFunc(object) { a: 1, b: 2, c: 3 } 26 | console.log('myFunc(map)', myFunc(myMap)); 27 | // -> myFunc(map) { a: 1, b: 2, c: 3 } 28 | -------------------------------------------------------------------------------- /02/04-the-of-method.js: -------------------------------------------------------------------------------- 1 | import { 2 | List, 3 | Map, 4 | Set, 5 | Seq 6 | } from 'immutable'; 7 | 8 | // The of() method is like a static class method. 9 | // You don't need to pass it an intermediary array 10 | // or object. Just the arguments used to build the 11 | // collection. 12 | const myList = List.of(1, 2, 3); 13 | const myMap = Map.of( 14 | 'a', 1, 15 | 'b', 2, 16 | 'c', 3 17 | ); 18 | const mySet = Set.of(1, 2, 3); 19 | const mySeq = Seq.of(1, 2, 3); 20 | 21 | // The end result is the same List/Map. 22 | console.log('myList', myList.toJS()); 23 | // -> myList [ 1, 2, 3 ] 24 | console.log('myMap', myMap.toJS()); 25 | // -> myMap { a: 1, b: 2, c: 3 } 26 | console.log('mySet', mySet.toJS()); 27 | // -> mySet [ 1, 2, 3 ] 28 | console.log('mySeq', mySeq.toJS()); 29 | // -> mySeq [ 1, 2, 3 ] 30 | -------------------------------------------------------------------------------- /02/05-the-fromjs-function.js: -------------------------------------------------------------------------------- 1 | // We don't need to import specific immutable 2 | // types. Just the function that knows how to 3 | // deeply parse everything we need. 4 | import { fromJS } from 'immutable'; 5 | 6 | // The fromJS() function knows how to parse simple 7 | // JSON-like data into immutable data types. 8 | const myList = fromJS([1, 2, 3]); 9 | const myMap = fromJS({ 10 | a: { 11 | b: ['c', 'd'], 12 | e: { 13 | f: 'g', 14 | h: 'i' 15 | } 16 | } 17 | }); 18 | 19 | // As usual, we can use get to read list data. 20 | console.log('myList', myList.get(0)); 21 | // -> myList 1 22 | 23 | // We can also read inside of the nested immutable 24 | // map to find what we nned. 25 | console.log( 26 | 'myMap nested list', 27 | myMap.getIn(['a', 'b']).toJS() 28 | ); 29 | // -> myMap nested list [ 'c', 'd' ] 30 | 31 | console.log('myMap nested value', myMap.getIn(['a', 'b', 1])); 32 | // -> myMap nested value d 33 | -------------------------------------------------------------------------------- /03/01-new-list-items.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | // The initial list of numbers. 4 | const myList = List.of(1, 2, 3); 5 | 6 | // Calling push() creates a new List instance. 7 | const myChangedList = myList.push(4); 8 | 9 | // myList is unchanged, myChangedList has the 10 | // new value. 11 | console.log('myList', myList.toJS()); 12 | // -> myList [ 1, 2, 3 ] 13 | console.log('myChangedList', myChangedList.toJS()); 14 | // -> myChangedList [ 1, 2, 3, 4 ] 15 | -------------------------------------------------------------------------------- /03/02-new-map-key-value-pairs.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | // The initial map of numbers. 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // Calling set() adds a new key-value pair to the 11 | // map by creating a new Map instance. 12 | const myChangedMap = myMap.set('four', 4); 13 | 14 | console.log('myMap', myMap.toJS()); 15 | // -> myMap { one: 1, two: 2, three: 3 } 16 | console.log('myChangedMap', myChangedMap.toJS()); 17 | // -> myChangedMap { one: 1, two: 2, three: 3, four: 4 } 18 | -------------------------------------------------------------------------------- /03/03-pushing-multiple-list-values.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | 5 | // We can chain together calls to push(), creating 6 | // intermediary collections that are garbage-collected 7 | // at the first opportunity. 8 | const myChangedList = myList 9 | .push(4) 10 | .push(5, 6) 11 | .push(7, 8) 12 | .push(9); 13 | 14 | console.log('myList', myList.toJS()); 15 | // -> myList [ 1, 2, 3 ] 16 | console.log('myChangedList', myChangedList); 17 | // -> myChangedList List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] 18 | -------------------------------------------------------------------------------- /03/04-setting-multiple-map-values.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of( 4 | 'one', 1, 5 | 'two', 2, 6 | 'three', 3 7 | ); 8 | const myChangedMap = myMap 9 | .set('four', 4) 10 | .set('five', 5) 11 | .set('six', 6); 12 | 13 | console.log('myMap', myMap.toJS()); 14 | // -> myMap { one: 1, two: 2, three: 3 } 15 | console.log('myChangedMap', myChangedMap.toJS()); 16 | // -> myChangedMap { one: 1, two: 2, 17 | // -> three: 3, four: 4, 18 | // -> five: 5, six: 6 } 19 | -------------------------------------------------------------------------------- /03/05-changing-list-items.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | // A single-item list - the number 1. 4 | const myList = List.of(1); 5 | 6 | // Calling set() with the index of the item to change 7 | // and the new value, creates a new list. 8 | const myChangedList = myList.set(0, 2); 9 | 10 | console.log('myList', myList.toJS()); 11 | // -> myList [ 1 ] 12 | console.log('myChangedList', myChangedList.toJS()); 13 | // -> myChangedList [ 2 ] 14 | -------------------------------------------------------------------------------- /03/06-updating-list-values.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('total', 0, 'step', 5), 5 | Map.of('total', 5, 'step', 10) 6 | ); 7 | 8 | // A function that increments the total key based 9 | // on the step key. 10 | const increment = map => map.set( 11 | 'total', 12 | map.get('total') + map.get('step') 13 | ); 14 | 15 | // By passing the increment() function to update(), 16 | // we can change list items based on their current value. 17 | const myChangedList = myList 18 | .update(0, increment) 19 | .update(1, increment); 20 | 21 | console.log('myList', myList.toJS()); 22 | // -> myList [ { total: 0, step: 5 }, { total: 5, step: 10 } ] 23 | console.log('myChangedList', myChangedList.toJS()); 24 | // -> myChangedList [ { total: 5, step: 5 }, { total: 15, step: 10 } ] 25 | -------------------------------------------------------------------------------- /03/07-changing-map-values.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | // A map with a single key-value pair. 4 | const myMap = Map.of('one', 1); 5 | 6 | // Calling set() to change the value of a given 7 | // key results in a new map. 8 | const myChangedMap = myMap.set('one', 'one'); 9 | 10 | console.log('myMap', myMap.toJS()); 11 | // -> myMap { one: 1 } 12 | console.log('myChangedMap', myChangedMap.toJS()); 13 | // -> myChangedMap { one: 'one' } 14 | -------------------------------------------------------------------------------- /03/08-updating-map-values.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('total', 0, 'step', 5), 5 | Map.of('total', 5, 'step', 10) 6 | ); 7 | 8 | // A function that increments the total key based 9 | // on the step key. 10 | const increment = map => map.update( 11 | 'total', 12 | t => t + map.get('step') 13 | ); 14 | 15 | // By passing the increment() function to update(), 16 | // we can change list items based on their current value. 17 | const myChangedList = myList 18 | .update(0, increment) 19 | .update(1, increment); 20 | 21 | console.log('myList', myList.toJS()); 22 | // -> myList [ { total: 0, step: 5 }, { total: 5, step: 10 } ] 23 | console.log('myChangedList', myChangedList.toJS()); 24 | // -> myChangedList [ { total: 5, step: 5 }, { total: 15, step: 10 } ] 25 | -------------------------------------------------------------------------------- /03/09-chaining-collection-mutation-methods.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of('one', 1, 'two', 2); 5 | 6 | // We can chain together insertion, and update 7 | // list methods. 8 | const myChangedList = myList 9 | .push(4) 10 | .set(3, 5) 11 | .update(3, n => n + 5); 12 | 13 | // We can chain together insertion, and update 14 | // map methods. 15 | const myChangedMap = myMap 16 | .set('three', 3) 17 | .set('three', 5) 18 | .update('three', t => t + 5); 19 | 20 | console.log('myList', myList.toJS()); 21 | // -> myList [ 1, 2, 3 ] 22 | console.log('myMap', myMap.toJS()); 23 | // -> myMap { one: 1, two: 2 } 24 | console.log('myChangedList', myChangedList.toJS()); 25 | // -> myChangedList [ 1, 2, 3, 10 ] 26 | console.log('myChangedMap', myChangedMap.toJS()); 27 | // -> myChangedMap { one: 1, two: 2, three: 10 } 28 | -------------------------------------------------------------------------------- /03/10-deleting-list-items.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | 5 | // Calling remove() will create a new List with the 6 | // item at the specified index removed. 7 | const myChangedList = myList.remove(0); 8 | 9 | console.log('myList', myList.toJS()); 10 | // -> myList [ 1, 2, 3 ] 11 | console.log('myChangedList', myChangedList.toJS()); 12 | // -> myChangedList [ 2, 3 ] 13 | -------------------------------------------------------------------------------- /03/11-deleting-map-key-value-pairs.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of( 4 | 'one', 1, 5 | 'two', 2, 6 | 'three', 3 7 | ); 8 | 9 | // Calling delete() will create a new Map 10 | // with the specified key-value removed. 11 | const myChangedMap = myMap.remove('one'); 12 | 13 | console.log('myMap', myMap.toJS()); 14 | // -> myMap { one: 1, two: 2, three: 3 } 15 | console.log('myChangedMap', myChangedMap.toJS()); 16 | // -> myChangedMap { three: 3, two: 2 } 17 | -------------------------------------------------------------------------------- /03/12-chaining-removal-methods.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3, 4); 4 | const myMap = Map.of( 5 | 'one', 1, 'two', 2, 6 | 'three', 3, 'four', 4, 7 | 'five', 5, 'six', 6 8 | ); 9 | 10 | // We're chaining two remove() calls together with the 11 | // same index argument because removing a list value 12 | // results in all values to the right, shifting to the 13 | // left by one index. 14 | const myChangedList = myList 15 | .remove(1) 16 | .remove(1); 17 | 18 | // With maps, we can chain remove() calls with one key 19 | // each, pass several keys to removeAll(), or both. 20 | const myChangedMap = myMap 21 | .remove('six') 22 | .removeAll(['five', 'four', 'three']); 23 | 24 | console.log('myList', myList.toJS()); 25 | // -> myList [ 1, 2, 3, 4 ] 26 | console.log('myMap', myMap.toJS()); 27 | // -> myMap { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6 } 28 | console.log('myChangedList', myChangedList.toJS()); 29 | // -> myChangedList [ 1, 4 ] 30 | console.log('myChangedMap', myChangedMap.toJS()); 31 | // -> myChangedMap { one: 1, two: 2 } 32 | -------------------------------------------------------------------------------- /03/13-deleting-by-replacing-collections.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Empties the collection by determining the 4 | // collection type, then retuning a new instance. 5 | const empty = (collection) => { 6 | if (collection instanceof List) { 7 | return List(); 8 | } 9 | if (collection instanceof Map) { 10 | return Map(); 11 | } 12 | return null; 13 | }; 14 | 15 | // List and Map instances. 16 | const myList = List.of(1, 2); 17 | const myMap = Map.of('one', 1, 'two', 2); 18 | 19 | // Create new instances by emptying the 20 | // existing instances. 21 | const myEmptyList = empty(myList); 22 | const myEmptyMap = empty(myMap); 23 | 24 | console.log('myList', myList.toJS()); 25 | // -> myList [ 1, 2 ] 26 | console.log('myEmptyList', myEmptyList.toJS()); 27 | // -> myEmptyList [] 28 | console.log('myMap', myMap.toJS()); 29 | // -> myMap { one: 1, two: 2 } 30 | console.log('myEmptyMap', myEmptyMap.toJS()); 31 | // -> myEmptyMap {} 32 | -------------------------------------------------------------------------------- /03/14-deleting-by-clearing-collections.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // List and Map instances. 4 | const myList = List.of(1, 2); 5 | const myMap = Map.of('one', 1, 'two', 2); 6 | 7 | // Create new instances by emptying the 8 | // existing instances. 9 | const myEmptyList = myList.clear(); 10 | const myEmptyMap = myMap.clear(); 11 | 12 | console.log('myList', myList.toJS()); 13 | // -> myList [ 1, 2 ] 14 | console.log('myEmptyList', myEmptyList.toJS()); 15 | // -> myEmptyList [] 16 | console.log('myMap', myMap.toJS()); 17 | // -> myMap { one: 1, two: 2 } 18 | console.log('myEmptyMap', myEmptyMap.toJS()); 19 | // -> myEmptyMap {} 20 | -------------------------------------------------------------------------------- /03/15-mutation-history-stacks.js: -------------------------------------------------------------------------------- 1 | import { List, Stack } from 'immutable'; 2 | 3 | // Names of List and Map methods that perform 4 | // persistent changes. These are the methods that 5 | // we want to build history from. 6 | const persistentChanges = [ 7 | 'set', 8 | 'delete', 9 | 'deleteAll', 10 | 'clear', 11 | 'update', 12 | 'merge', 13 | 'mergeWith', 14 | 'mergeDeep', 15 | 'mergeDeepWith', 16 | 'setIn', 17 | 'deleteIn', 18 | 'updateIn', 19 | 'mergeIn', 20 | 'mergeDeepIn', 21 | 'insert', 22 | 'push', 23 | 'pop', 24 | 'unshift', 25 | 'shift', 26 | 'setSize' 27 | ]; 28 | 29 | // Where the history for a given collection is stored. 30 | const mutations = new WeakMap(); 31 | 32 | // Defines proxy behavior for collection instances. 33 | // It's a way to trap method calls and redirect them 34 | // to instances in the mutations map. 35 | const historyHandler = { 36 | get(target, key) { 37 | // Before we do anything, make sure that the 38 | // target collection has a Stack instance in 39 | // the mutations map. 40 | if (!mutations.has(target)) { 41 | mutations.set( 42 | target, 43 | Stack().unshift(target) 44 | ); 45 | } 46 | 47 | // Get the mutation history for this collection. 48 | const stack = mutations.get(target); 49 | 50 | // Check if the caller is calling one of the 51 | // recognized persistent change methods. 52 | if (persistentChanges.includes(key)) { 53 | // Return a function that calls the method in question 54 | // on the most recent stack item. 55 | return (...args) => { 56 | const result = stack.first()[key](...args); 57 | 58 | // Store the result as the newest item on the stack. 59 | mutations.set( 60 | target, 61 | stack.unshift(result) 62 | ); 63 | 64 | // Return the result like normal. 65 | return result; 66 | }; 67 | 68 | // The caller is calling the undo() method. Remove the 69 | // first item on the stack, if there's more than 70 | // one item. 71 | } else if (key === 'undo') { 72 | return () => 73 | mutations.set( 74 | target, 75 | stack.count() > 1 ? stack.shift() : stack 76 | ); 77 | } 78 | // Not a persistent change method, just call it and 79 | // return the result. 80 | return stack.first()[key]; 81 | } 82 | }; 83 | 84 | // Wraps a List instance with the historyHandler proxy. 85 | const myList = new Proxy(List.of(1, 2, 3), historyHandler); 86 | console.log('myList', myList.toJS()); 87 | // -> myList [ 1, 2, 3 ] 88 | 89 | myList.push(4); 90 | console.log('push(4)', myList.toJS()); 91 | // -> push(4) [ 1, 2, 3, 4 ] 92 | myList.delete(0); 93 | console.log('delete(0)', myList.toJS()); 94 | // -> delete(0) [ 2, 3, 4 ] 95 | myList.undo(); 96 | console.log('undo()', myList.toJS()); 97 | // -> undo() [ 1, 2, 3, 4 ] 98 | myList.undo(); 99 | console.log('undo()', myList.toJS()); 100 | // -> undo() [ 1, 2, 3 ] 101 | -------------------------------------------------------------------------------- /04/01-filtering-by-value.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // The filter callback function that's applied to each 11 | // item in the collection. If the function returns true, 12 | // the item is included in the resulting collection. 13 | const filter = i => i === 1 || i === 2; 14 | 15 | // The same filter function can be used to filter list 16 | // items and map values. 17 | const myFilteredList = myList.filter(filter); 18 | const myFilteredMap = myMap.filter(filter); 19 | 20 | console.log('myList', myList.toJS()); 21 | // -> myList [ 1, 2, 3 ] 22 | console.log('myFilteredList', myFilteredList.toJS()); 23 | // -> myFilteredList [ 1, 2 ] 24 | 25 | console.log('myMap', myMap.toJS()); 26 | // -> myMap { one: 1, two: 2, three: 3 } 27 | console.log('myFilteredMap', myFilteredMap.toJS()); 28 | // -> myFilteredMap { one: 1, two: 2 } 29 | -------------------------------------------------------------------------------- /04/02-filtering-by-greater-lesser.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // Filters values that are less than 3. 11 | const myFilteredList = myList.filter(i => i < 3); 12 | 13 | // Filters values that are greater than 1. 14 | const myFilteredMap = myMap.filter(i => i > 1); 15 | 16 | // Uses both greater and lesser than operations to 17 | // produce a callback function that checks in the 18 | // value falls within a range. 19 | const rangeFilter = (min, max) => v => v > min && v < max; 20 | const myFilteredByRange = myList.filter(rangeFilter(1, 3)); 21 | 22 | console.log('myList', myList.toJS()); 23 | // -> myList [ 1, 2, 3 ] 24 | console.log('myFilteredList', myFilteredList.toJS()); 25 | // -> myFilteredList [ 1, 2 ] 26 | console.log('myMap', myMap.toJS()); 27 | // -> myMap { one: 1, two: 2, three: 3 } 28 | console.log('myFilteredMap', myFilteredMap.toJS()); 29 | // -> myFilteredMap { two: 2, three: 3 } 30 | console.log('myFilteredByRange', myFilteredByRange.toJS()); 31 | // -> myFilteredByRange [ 2 ] 32 | -------------------------------------------------------------------------------- /04/03-filtering-by-negation.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of( 4 | 'one', 1, 5 | 'two', 2, 6 | 'three', 3 7 | ); 8 | 9 | // Returns truthy if the argument is odd. 10 | const myFilter = i => i % 2; 11 | 12 | // Filters the odd values in in the collection. 13 | const myOddsMap = myMap.filter(myFilter); 14 | // Filters the even values by negating the filter() result. 15 | const myEvensMap = myMap.filterNot(myFilter); 16 | 17 | console.log('myMap', myMap.toJS()); 18 | // -> myMap { one: 1, two: 2, three: 3 } 19 | console.log('myOddsMap', myOddsMap.toJS()); 20 | // -> myOddsMap { one: 1, three: 3 } 21 | console.log('myEvensMap', myEvensMap.toJS()); 22 | // -> myEvensMap { two: 2 } 23 | -------------------------------------------------------------------------------- /04/04-filtering-maps-by-keys.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of( 4 | 'one', 1, 5 | 'two', 2, 6 | 'three', 3 7 | ); 8 | 9 | // The key of the key/value pair is the second 10 | // argument. We're looking for keys the have an "o". 11 | const myFilteredMap = myMap.filter( 12 | (v, k) => k.includes('o') 13 | ); 14 | 15 | console.log('myMap', myMap.toJS()); 16 | // -> myMap { one: 1, two: 2, three: 3 } 17 | console.log('myFilteredMap', myFilteredMap.toJS()); 18 | // -> myFilteredMap { one: 1, two: 2 } 19 | -------------------------------------------------------------------------------- /04/05-filtering-by-fancy-keys.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | // Immutable.js Map keys can be anything, including other maps. 4 | const myFancyMap = Map.of( 5 | Map.of('name', 'one'), 1, 6 | Map.of('name', 'two'), 2, 7 | Map.of('name', 'three'), 3 8 | ); 9 | 10 | // If map keys are anything fancier than strings, we might have 11 | // to filter using specific properties. In this case, we're using 12 | // the name property of the Map. 13 | const myFilteredMap = myFancyMap.filter( 14 | (v, k) => k.get('name').startsWith('t') 15 | ); 16 | 17 | console.log('myFancyMap', myFancyMap.toJS()); 18 | // -> myFancyMap 19 | // -> { 'Map { "name": "one" }': 1, 20 | // -> 'Map { "name": "two" }': 2, 21 | // -> 'Map { "name": "three" }': 3 } 22 | console.log('myFilteredMap', myFilteredMap.toJS()); 23 | // -> myFilteredMap 24 | // -> { 'Map { "name": "two" }': 2, 25 | // -> 'Map { "name": "three" }': 3 } 26 | -------------------------------------------------------------------------------- /04/06-existence-checks.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // The has() method checks for indexes that exist in lists. 11 | const myListHas2 = myList.has(2); 12 | const myListHas3 = myList.has(3); 13 | // The has() method checks for keys that exist in maps. 14 | const myMapHasThree = myMap.has('three'); 15 | const myMapHasFour = myMap.has('four'); 16 | // The includes() method checks for values that exist in 17 | // lists or maps. 18 | const myListIncludes3 = myList.includes(3); 19 | const myListIncludes4 = myList.includes(4); 20 | const myMapIncludes3 = myMap.includes(3); 21 | const myMapIncludes4 = myMap.includes(4); 22 | 23 | console.log('myListHas2', myListHas2); 24 | // -> myListHas2 true 25 | console.log('myListHas3', myListHas3); 26 | // -> myListHas3 false 27 | 28 | console.log('myMapHasThree', myMapHasThree); 29 | // -> myMapHasThree true 30 | console.log('myMapHasFour', myMapHasFour); 31 | // -> myMapHasFour false 32 | 33 | console.log('myListIncludes3', myListIncludes3); 34 | // -> myListIncludes3 true 35 | console.log('myListIncludes4', myListIncludes4); 36 | // -> myListIncludes4 false 37 | console.log('myMapIncludes3', myMapIncludes3); 38 | // -> myMapIncludes3 true 39 | console.log('myMapIncludes4', myMapIncludes4); 40 | // -> myMapIncludes4 false 41 | -------------------------------------------------------------------------------- /04/07-finding-items.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // Finding collection items using the find() method. The predicate 11 | // function returns true if it's the item we're looking for. 12 | // If not item is found, undefined is returned. 13 | const myFoundListItem = myList.find(v => v === 3); 14 | const myNotFoundListItem = myList.find(v => v === 4); 15 | const myFoundMapItem = myMap.find(v => v === 3); 16 | const myNotFoundMapItem = myMap.find(v => v === 4); 17 | 18 | console.log('myFoundListItem', myFoundListItem); 19 | // -> myFoundListItem 3 20 | console.log('myNotFoundListItem', myNotFoundListItem); 21 | // -> myNotFoundListItem undefined 22 | console.log('myFoundMapItem', myFoundMapItem); 23 | // -> myFoundMapItem 3 24 | console.log('myNotFoundMapItem', myNotFoundMapItem); 25 | // -> myNotFoundMapItem undefined 26 | -------------------------------------------------------------------------------- /04/08-the-is-function.js: -------------------------------------------------------------------------------- 1 | import { is, List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2); 4 | const myMap = Map.of('one', 1); 5 | 6 | // The is() function is fundamental to comparing Immutable.js 7 | // structures to other Immutable.js structures. Items are compared 8 | // deeply, and since the data is Immutable, these comparisons 9 | // can be extremely efficient. 10 | console.log('myList', myList.toJS()); 11 | // -> myList [ 1, 2 ] 12 | console.log('is([1, 2])', is(myList, List.of(1, 2))); 13 | // -> is([1, 2]) true 14 | console.log('equals([1, 2])', myList.equals(List.of(1, 2))); 15 | // -> equals([1, 2]) true 16 | console.log('is([1, 1])', is(myList, List.of(1, 1))); 17 | // -> is([1, 1]) false 18 | console.log('equals([1, 1])', myList.equals(List.of(1, 1))); 19 | // -> equals([1, 1]) false 20 | console.log('is([2, 1])', is(myList, List.of(2, 1))); 21 | // -> is([2, 1]) false 22 | console.log('equals([2, 1])', myList.equals(List.of(2, 1))); 23 | // -> equals([2, 1]) false 24 | 25 | console.log('myMap', myMap.toJS()); 26 | // -> myMap { one: 1 } 27 | console.log('is({ one: 1 })', is(myMap, Map.of('one', 1))); 28 | // -> is({ one: 1 }) true 29 | console.log('equals({ one: 1 })', myMap.equals(Map.of('one', 1))); 30 | // -> equals({ one: 1 }) true 31 | console.log('is({ two: 1 })', is(myMap, Map.of('two', 1))); 32 | // -> is({ two: 1 }) false 33 | console.log('equals({ two: 1 })', myMap.equals(Map.of('two', 1))); 34 | // -> equals({ two: 1 }) false 35 | console.log('is({ one: 2 })', is(myMap, Map.of('one', 2))); 36 | // -> is({ one: 2 }) false 37 | console.log('equals({ one: 1 })', myMap.equals(Map.of('one', 2))); 38 | // -> equals({ one: 1 }) false 39 | -------------------------------------------------------------------------------- /04/09-searching-lists-of-maps.js: -------------------------------------------------------------------------------- 1 | import { 2 | List, 3 | Map 4 | } from 'immutable'; 5 | 6 | // A utility for creating callback functions that use 7 | // Collection.equals() for equality checking. 8 | const is = a => b => a.equals(b); 9 | 10 | const myList = List.of( 11 | Map.of('one', 1), 12 | Map.of('two', 2), 13 | Map.of('three', 3) 14 | ); 15 | 16 | // The includes() method uses Immutable.js is() for equality. 17 | // We can use our is() utility function when we want the same 18 | // type of equality checking for filter operations. 19 | const includesOne = myList.includes(Map.of('one', 1)); 20 | const myFilteredList = myList.filterNot(is(Map.of('one', 1))); 21 | 22 | console.log('includesOne', includesOne); 23 | // -> includesOne true 24 | console.log('myFilteredList', myFilteredList.toJS()); 25 | // -> myFilteredList [ { two: 2 }, { three: 3 } ] 26 | -------------------------------------------------------------------------------- /04/10-partial-map-search-matches.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // This utility produces a function that compares a 4 | // map to another map. The supplied map is checked 5 | // against the map that's passed to the returned 6 | // function when called. The comparison is done 7 | // by converting both maps to lists of key/pair lists, 8 | // then using the isSubset() method. 9 | const mapIncludes = subset => superset => 10 | subset 11 | .entrySeq() 12 | .map(List) 13 | .isSubset( 14 | superset 15 | .entrySeq() 16 | .map(List) 17 | ); 18 | 19 | const myList = List.of( 20 | Map.of('one', 1, 'two', 2, 'three', 3), 21 | Map.of('one', 1, 'two', 2, 'four', 4), 22 | Map.of('one', 1, 'three', 3, 'four', 4) 23 | ); 24 | 25 | // We can pass simple maps to compare, without the need 26 | // to write a bunch of comparison logic. 27 | const filteredOneTwo = myList.filter( 28 | mapIncludes(Map.of('one', 1, 'two', 2)) 29 | ); 30 | const filteredFourFive = myList.filter( 31 | mapIncludes(Map.of('four', 4, 'five', 5)) 32 | ); 33 | 34 | console.log('myList', myList.toJS()); 35 | // -> myList 36 | // -> [ { one: 1, two: 2, three: 3 }, 37 | // -> { one: 1, two: 2, four: 4 }, 38 | // -> { one: 1, three: 3, four: 4 } ] 39 | console.log('filteredOneTwo', filteredOneTwo.toJS()); 40 | // -> filteredOneTwo 41 | // -> [ { one: 1, two: 2, three: 3 }, 42 | // -> { one: 1, two: 2, four: 4 } ] 43 | console.log('filteredFourFive', filteredFourFive.toJS()); 44 | // -> filteredFourFive [] 45 | -------------------------------------------------------------------------------- /04/11-search-direction.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of('apples', 'bananas', 'cherries', 'chocolate'); 4 | 5 | // A search predicate that looks for items that start 6 | // with "ch". 7 | const predicate = s => s.startsWith('ch'); 8 | 9 | // Finding a single item in an ordered list starting from 10 | // right to left. 11 | const myFoundItem = myList.findLast(predicate); 12 | 13 | // Filtering out a list of items from right to 14 | // left is a little trickier since we want to preserve 15 | // the left to right ordering in the result. 16 | const myFilteredList = myList.reduceRight( 17 | (result, v) => predicate(v) ? 18 | result.unshift(v) : result, 19 | List() 20 | ); 21 | 22 | console.log('myList', myList.toJS()); 23 | // -> myList [ 'apples', 'bananas', 'cherries', 'chocolate' ] 24 | console.log('myFoundItem', myFoundItem); 25 | // -> myFoundItem chocolate 26 | console.log('myFilteredList', myFilteredList.toJS()); 27 | // -> myFilteredList [ 'cherries', 'chocolate' ] 28 | -------------------------------------------------------------------------------- /05/01-basic-sequence-creation.js: -------------------------------------------------------------------------------- 1 | import { Seq } from 'immutable'; 2 | 3 | // Passing an array to Seq() creates and indexed 4 | // sequence. Passing an object to Seq() creates 5 | // a keyed sequence. 6 | const myIndexedSeq = Seq([1, 2, 3]); 7 | const myKeyedSeq = Seq({ one: 1, two: 2, three: 3 }); 8 | 9 | console.log('myIndexedSeq', myIndexedSeq.toJS()); 10 | // -> myIndexedSeq [ 1, 2, 3 ] 11 | console.log('myKeyedSeq', myKeyedSeq.toJS()); 12 | // -> myKeyedSeq { one: 1, two: 2, three: 3 } 13 | -------------------------------------------------------------------------------- /05/02-collections-to-sequences.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // Sequences often start out as lists or maps, 11 | // and are then turned into sequences before chains 12 | // of lazy operations are called. 13 | const myIndexedSeq = myList.toSeq(); 14 | const myKeyedSeq = myMap.toSeq(); 15 | 16 | console.log('myIndexedSeq', myIndexedSeq.toJS()); 17 | // -> myIndexedSeq [ 1, 2, 3 ] 18 | console.log('myKeyedSeq', myKeyedSeq.toJS()); 19 | // -> myKeyedSeq { one: 1, two: 2, three: 3 } 20 | -------------------------------------------------------------------------------- /05/03-for-loop-iteration.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | for (let item of myList.toSeq()) { 11 | console.log('myList', item); 12 | // -> myList 1 13 | // -> myList 2 14 | // -> myList 3 15 | } 16 | 17 | for (let item of myMap.toSeq()) { 18 | console.log('myMap', item); 19 | // -> myMap [ 'one', 1 ] 20 | // -> myMap [ 'two', 2 ] 21 | // -> myMap [ 'three', 3 ] 22 | } 23 | -------------------------------------------------------------------------------- /05/04-the-for-each-iterator.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // We go straight from a list, to iterating over 11 | // an indexed sequence. 12 | myList 13 | .toSeq() 14 | .forEach(item => console.log('myList', item)); 15 | // -> myList 1 16 | // -> myList 2 17 | // -> myList 3 18 | 19 | // We go straight from a map, to iterating over 20 | // a keyed sequence. 21 | myMap 22 | .toSeq() 23 | .forEach(item => console.log('myMap', item)); 24 | // -> myMap 1 25 | // -> myMap 2 26 | // -> myMap 3 27 | -------------------------------------------------------------------------------- /05/05-sequence-filters.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3, 4, 5, 6); 4 | const myMap = Map.of( 5 | 'one', 1, 'two', 2, 6 | 'three', 3, 'four', 4, 7 | 'five', 5, 'six', 6 8 | ); 9 | 10 | // A simple filter predicate that will return 11 | // odd numbers. We're logging the call here 12 | // so that we can see where it's called relative 13 | // to the forEach() callback. 14 | const predicate = (item) => { 15 | console.log('filtering', item); 16 | return item % 2; 17 | }; 18 | 19 | // By converting to a sequence before filtering, 20 | // we enable lazy evaluation. The forEach() callback 21 | // "pulls" one item through the sequence at a time. 22 | myList 23 | .toSeq() 24 | .filter(predicate) 25 | .forEach(item => console.log('myList', item)); 26 | // -> filtering 1 27 | // -> myList 1 28 | // -> filtering 2 29 | // -> filtering 3 30 | // -> myList 3 31 | // -> filtering 4 32 | // -> filtering 5 33 | // -> myList 5 34 | // -> filtering 6 35 | 36 | // We get identical results with maps. 37 | myMap 38 | .toSeq() 39 | .filter(predicate) 40 | .forEach(item => console.log('myMap', item)); 41 | // -> filtering 1 42 | // -> myMap 1 43 | // -> filtering 2 44 | // -> filtering 3 45 | // -> myMap 3 46 | // -> filtering 4 47 | // -> filtering 5 48 | // -> myMap 5 49 | // -> filtering 6 50 | -------------------------------------------------------------------------------- /05/06-multiple-sequence-filters.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | // Equality predicate builder. Includes output 4 | // of comparisons as they're made so we can see 5 | // them relative to the output. 6 | const equals = first => (second) => { 7 | console.log(`${first} === ${second}`); 8 | return first === second; 9 | }; 10 | 11 | const myList = List.of(1, 2, 3, 4, 5, 6); 12 | 13 | // It's easy to assemble complex filtering logic, 14 | // without having to create intermediary representations. 15 | // The filters are executed one at a time, in order, 16 | // for each item in the collection. 17 | myList 18 | .toSeq() 19 | .filterNot(equals(1)) 20 | .filterNot(equals(3)) 21 | .filterNot(equals(5)) 22 | .forEach(item => console.log('myList', item)); 23 | // -> 1 === 1 24 | // -> 1 === 2 25 | // -> 3 === 2 26 | // -> 5 === 2 27 | // -> myList 2 28 | // -> 1 === 3 29 | // -> 3 === 3 30 | // -> 1 === 4 31 | // -> 3 === 4 32 | // -> 5 === 4 33 | // -> myList 4 34 | // -> 1 === 5 35 | // -> 3 === 5 36 | // -> 5 === 5 37 | // -> 1 === 6 38 | // -> 3 === 6 39 | // -> 5 === 6 40 | // -> myList 6 41 | -------------------------------------------------------------------------------- /05/07-removing-work-with-take.js: -------------------------------------------------------------------------------- 1 | import { Range } from 'immutable'; 2 | 3 | // Filter to include odd numbers 4 | const predicate = i => i % 2; 5 | 6 | // An infinate range sequence. 7 | const myRange = Range(); 8 | 9 | // The resulting forEach() iteratee will always 10 | // be called 5 times, since take() is applied 11 | // after filter(). 12 | myRange 13 | .filter(predicate) 14 | .take(5) 15 | .forEach((item) => { 16 | console.log('myRange (filter then take)', item); 17 | }); 18 | // -> myRange (filter then take) 1 19 | // -> myRange (filter then take) 3 20 | // -> myRange (filter then take) 5 21 | // -> myRange (filter then take) 7 22 | // -> myRange (filter then take) 9 23 | 24 | // 5 items are taken from the sequence before 25 | // the filter() is applied. This means that the 26 | // forEach() iteratee could be called fewer than 27 | // 5 times. 28 | myRange 29 | .take(5) 30 | .filter(predicate) 31 | .forEach((item) => { 32 | console.log('myRange (take then filter)', item); 33 | }); 34 | // -> myRange (take then filter) 1 35 | // -> myRange (take then filter) 3 36 | -------------------------------------------------------------------------------- /05/08-pagination-using-slice.js: -------------------------------------------------------------------------------- 1 | import { Range } from 'immutable'; 2 | 3 | // Filter to include even numbers 4 | const predicate = i => i % 2 === 0; 5 | 6 | // An infinite sequence of even numbers. 7 | const myRange = Range().filter(predicate); 8 | 9 | // Using slice() to create "pages" from our 10 | // infinite number sequence. 11 | const page1 = myRange.slice(0, 5); 12 | const page2 = myRange.slice(5, 10); 13 | const page3 = myRange.slice(10, 15); 14 | 15 | // The slice() and the filter() calls aren't applied 16 | // until we iterate over each page. 17 | page1.forEach(i => console.log('page1', i)); 18 | // -> page1 0 19 | // -> page1 2 20 | // -> page1 4 21 | // -> page1 6 22 | // -> page1 8 23 | 24 | page2.forEach(i => console.log('page2', i)); 25 | // -> page2 10 26 | // -> page2 12 27 | // -> page2 14 28 | // -> page2 16 29 | // -> page2 18 30 | 31 | page3.forEach(i => console.log('page3', i)); 32 | // -> page3 20 33 | // -> page3 22 34 | // -> page3 24 35 | // -> page3 26 36 | // -> page3 28 37 | -------------------------------------------------------------------------------- /06/01-deafult-sorting.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(2, 1, 4, 3); 4 | 5 | // This works the same way as default sorting 6 | // with JavaScript arrays. The main difference is 7 | // that this creates a new list. 8 | const mySortedList = myList.sort(); 9 | 10 | console.log('myList', myList.toJS()); 11 | // -> myList [ 2, 1, 4, 3 ] 12 | 13 | console.log('mySortedList', mySortedList.toJS()); 14 | // -> mySortedList [ 1, 2, 3, 4 ] 15 | -------------------------------------------------------------------------------- /06/02-reversed-sorting.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(2, 1, 4, 3); 4 | 5 | // Reversing does exactly that. It reverses the current 6 | // order of the list items. So unless you're absolutely 7 | // certain about the current sort order, this 8 | // method could have unintended consequences. 9 | const myReversedList = myList.reverse(); 10 | 11 | // If we want the expected behavior of reverse(), 12 | // we have to sort() the collection first. 13 | const mySortedReversedList = myList.sort().reverse(); 14 | 15 | // Sorting backwards will actually perform a sort, 16 | // instead of just reversing the current order. 17 | const myBackwardSortedList = myList.sortBy(i => -i); 18 | 19 | console.log('myList', myList.toJS()); 20 | // -> myList [ 2, 1, 4, 3 ] 21 | 22 | console.log('myReversedList', myReversedList.toJS()); 23 | // -> myReversedList [ 3, 4, 1, 2 ] 24 | 25 | console.log('mySortedReversedList', mySortedReversedList.toJS()); 26 | // -> mySortedReversedList [ 4, 3, 2, 1 ] 27 | 28 | console.log('myBackwardSortedList', myBackwardSortedList.toJS()); 29 | // -> myBackwardSortedList [ 4, 3, 2, 1 ] 30 | -------------------------------------------------------------------------------- /06/03-sorting-lists-of-maps.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Utility to create a simple sortBy() callback. 4 | const prop = p => map => map.get(p); 5 | 6 | // Utility to create a sort() callback. This is useful 7 | // when we need to sort lists of maps by more than one 8 | // property. We keep iterating over the properties 9 | // until we find two that aren't equal. 10 | const comp = (order, ...props) => (a, b) => { 11 | for (const p of props) { 12 | if (a.get(p) > b.get(p)) { 13 | return order * 1; 14 | } 15 | 16 | if (a.get(p) < b.get(p)) { 17 | return order * -1; 18 | } 19 | } 20 | return 0; 21 | }; 22 | 23 | const asc = (...props) => comp(1, ...props); 24 | const desc = (...props) => comp(-1, ...props); 25 | 26 | const myList = List.of( 27 | Map.of('name', 'ccc', 'age', 23), 28 | Map.of('name', 'aaa', 'age', 23), 29 | Map.of('name', 'ddd', 'age', 19), 30 | Map.of('name', 'bbb', 'age', 19) 31 | ); 32 | 33 | // Sorting by a single property, using sortBy() and prop(). 34 | const byAge = myList 35 | .sortBy(prop('age')); 36 | 37 | // Sorting by multiple properties, using sort() and asc(). 38 | const byAgeAndName = myList 39 | .sort(asc('age', 'name')); 40 | 41 | // Sorting by multiple properties, using sort() and desc(). 42 | const byAgeAndNameDesc = myList 43 | .sort(desc('age', 'name')); 44 | 45 | console.log('myList', myList.toJS()); 46 | // -> myList 47 | // -> [ { name: 'ccc', age: 23 }, 48 | // -> { name: 'aaa', age: 23 }, 49 | // -> { name: 'ddd', age: 19 }, 50 | // -> { name: 'bbb', age: 19 } ] 51 | 52 | console.log('byAge', byAge.toJS()); 53 | // -> byAge 54 | // -> [ { name: 'ddd', age: 19 }, 55 | // -> { name: 'bbb', age: 19 }, 56 | // -> { name: 'ccc', age: 23 }, 57 | // -> { name: 'aaa', age: 23 } ] 58 | 59 | console.log('byAgeAndName', byAgeAndName.toJS()); 60 | // -> byAgeAndName [ { name: 'ccc', age: 23 } 61 | // -> { name: 'aaa', age: 23 }, 62 | // -> { name: 'ddd', age: 19 }, 63 | // -> { name: 'bbb', age: 19 } ] 64 | 65 | console.log('byAgeAndNameDesc', byAgeAndNameDesc.toJS()); 66 | // -> byAgeAndNameDesc [ { name: 'bbb', age: 19 }, 67 | // -> { name: 'ddd', age: 19 }, 68 | // -> { name: 'aaa', age: 23 }, 69 | // -> { name: 'ccc', age: 23 } ] 70 | -------------------------------------------------------------------------------- /06/04-ordering-maps.js: -------------------------------------------------------------------------------- 1 | import { Map, OrderedMap } from 'immutable'; 2 | 3 | // Maps generally maintain the set() order, but this 4 | // isn't guaranteed during iteration. 5 | const myMap = Map() 6 | .set('one', 1) 7 | .set('two', 2) 8 | .set('three', 3); 9 | 10 | myMap 11 | .forEach(i => console.log('myMap', i)); 12 | // -> myMap 1 13 | // -> myMap 2 14 | // -> myMap 3 15 | 16 | // Ordered maps enforce set() order, including the 17 | // iteration order of items. 18 | const myOrderedMap = OrderedMap() 19 | .set('three', 3) 20 | .set('two', 2) 21 | .set('one', 1); 22 | 23 | myOrderedMap 24 | .forEach(i => console.log('myOrderedMap', i)); 25 | // -> myOrderedMap 3 26 | // -> myOrderedMap 2 27 | // -> myOrderedMap 1 28 | -------------------------------------------------------------------------------- /06/05-sorting-maps.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of( 4 | 'three', 3, 5 | 'one', 1, 6 | 'four', 4, 7 | 'two', 2 8 | ); 9 | 10 | // We can sort maps using the sort() method, but 11 | // to preserve the sort order, we need to convert 12 | // it to an ordered map. 13 | const mySortedMap = myMap 14 | .toOrderedMap() 15 | .sort(); 16 | 17 | // Using the sort() method sorts maps by value. Using 18 | // sortBy(), we can pass a simple callback that allows 19 | // us to sort by key. 20 | const mySortedByKeyMap = myMap 21 | .toOrderedMap() 22 | .sortBy((v, k) => k); 23 | 24 | myMap.forEach( 25 | (v, k) => console.log('myMap', `${k} => ${v}`) 26 | ); 27 | // -> myMap three => 3 28 | // -> myMap one => 1 29 | // -> myMap four => 4 30 | // -> myMap two => 2 31 | 32 | 33 | mySortedMap.forEach( 34 | (v, k) => console.log('mySortedMap', `${k} => ${v}`) 35 | ); 36 | // -> mySortedMap one => 1 37 | // -> mySortedMap two => 2 38 | // -> mySortedMap three => 3 39 | // -> mySortedMap four => 4 40 | 41 | mySortedByKeyMap.forEach( 42 | (v, k) => console.log('mySortedByKeyMap', `${k} => ${v}`) 43 | ); 44 | // -> mySortedByKeyMap four => 4 45 | // -> mySortedByKeyMap one => 1 46 | // -> mySortedByKeyMap three => 3 47 | // -> mySortedByKeyMap two => 2 48 | -------------------------------------------------------------------------------- /06/06-maintaining-sort-order.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | // With a given item and a given list, we can 4 | // figure out the insertion index that would 5 | // maintain sort order of the list, then use 6 | // the insert() method to insert the item in 7 | // the correct order. 8 | const sortedInsert = (item, list) => { 9 | let low = 0; 10 | let high = list.count(); 11 | 12 | while (low < high) { 13 | const mid = (low + high) >>> 1; 14 | if (item > list.get(mid)) { 15 | low = mid + 1; 16 | } else { 17 | high = mid; 18 | } 19 | } 20 | 21 | return list.insert(low, item); 22 | }; 23 | 24 | const myList = List.of(1, 3, 5); 25 | const withTwo = sortedInsert(2, myList); 26 | const withFour = sortedInsert(4, withTwo); 27 | const usingSort = myList 28 | .push(2) 29 | .push(4) 30 | .sort(); 31 | 32 | console.log('myList', myList.toJS()); 33 | // -> myList [ 1, 3, 5 ] 34 | console.log('withTwo', withTwo.toJS()); 35 | // -> withTwo [ 1, 2, 3, 5 ] 36 | console.log('withFour', withFour.toJS()); 37 | // -> withFour [ 1, 2, 3, 4, 5 ] 38 | console.log('usingSort', usingSort.toJS()); 39 | // -> usingSort [ 1, 2, 3, 4, 5 ] 40 | -------------------------------------------------------------------------------- /07/01-plucking-property-values.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('first', 1, 'second', 2), 5 | Map.of('first', 3, 'second', 4), 6 | Map.of('first', 5, 'second', 6) 7 | ); 8 | 9 | // Mapping Immutable.js lists works the same as 10 | // mapping JavaScript arrays. Here, we're producing 11 | // a new list and we're using get() for each map 12 | // item in the list to get the value we're after. 13 | const myMappedList = myList 14 | .map(v => v.get('second')); 15 | 16 | console.log('myList', myList.toJS()); 17 | // -> myList [ 18 | // -> { first: 1, second: 2 }, 19 | // -> { first: 3, second: 4 }, 20 | // -> { first: 5, second: 6 } ] 21 | 22 | console.log('myMappedList', myMappedList.toJS()); 23 | // -> myMappedList [ 2, 4, 6 ] 24 | -------------------------------------------------------------------------------- /07/02-computing-new-values.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Capitalizes the first letter of a given string. 4 | const capitalize = s => 5 | `${s.charAt(0).toUpperCase()}${s.slice(1)}`; 6 | 7 | const myList = List.of( 8 | Map.of('first', 'joe', 'last', 'brown', 'age', 45), 9 | Map.of('first', 'john', 'last', 'smith', 'age', 32), 10 | Map.of('first', 'mary', 'last', 'wise', 'age', 56) 11 | ); 12 | 13 | // Instead of mapping the list item to a value 14 | // that already exists within the item, we're 15 | // computing a new value. In this case, we need 16 | // the capitalized name string instead of two 17 | // string properties. 18 | const myMappedList = myList.map( 19 | v => [ 20 | capitalize(v.get('first')), 21 | capitalize(v.get('last')) 22 | ].join(' ') 23 | ); 24 | 25 | console.log('myList', myList.toJS()); 26 | // -> myList [ { first: 'joe', last: 'brown', age: 45 }, 27 | // -> { first: 'john', last: 'smith', age: 32 }, 28 | // -> { first: 'mary', last: 'wise', age: 56 } ] 29 | 30 | console.log('myMappedList', myMappedList.toJS()); 31 | // -> myMappedList [ 'Joe Brown', 'John Smith', 'Mary Wise' ] 32 | -------------------------------------------------------------------------------- /07/03-mapping-new-properties.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Capitalizes the first letter of a given string. 4 | const capitalize = s => 5 | `${s.charAt(0).toUpperCase()}${s.slice(1)}`; 6 | 7 | const myList = List.of( 8 | Map.of('first', 'joe', 'last', 'brown', 'age', 45), 9 | Map.of('first', 'john', 'last', 'smith', 'age', 32), 10 | Map.of('first', 'mary', 'last', 'wise', 'age', 56) 11 | ); 12 | 13 | // Instead of mapping the list of maps to a list 14 | // of name strings, we map it to a new list of maps. 15 | // We're simply adding a new name property to each 16 | // item. 17 | const myMappedList = myList.map( 18 | v => v.set('name', [ 19 | capitalize(v.get('first')), 20 | capitalize(v.get('last')) 21 | ].join(' ')) 22 | ); 23 | 24 | console.log('myList', myList.toJS()); 25 | // -> myList [ { first: 'joe', last: 'brown', age: 45 }, 26 | // -> { first: 'john', last: 'smith', age: 32 }, 27 | // -> { first: 'mary', last: 'wise', age: 56 } ] 28 | 29 | console.log('myMappedList', myMappedList.toJS()); 30 | // -> myMappedList [ { first: 'joe', last: 'brown', age: 45, name: 'Joe Brown' }, 31 | // -> { first: 'john', last: 'smith', age: 32, name: 'John Smith' }, 32 | // -> { first: 'mary', last: 'wise', age: 56, name: 'Mary Wise' } ] 33 | -------------------------------------------------------------------------------- /07/04-mapping-specific-properties.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Returns a new map that only includes the given 4 | // property names. 5 | const pick = (...props) => map => map 6 | .filter((v, k) => props.includes(k)); 7 | 8 | // Returns a new map that omits the given 9 | // property names. 10 | const omit = (...props) => map => map 11 | .filterNot((v, k) => props.includes(k)); 12 | 13 | const myList = List.of( 14 | Map.of('first', 'joe', 'last', 'brown', 'age', 45), 15 | Map.of('first', 'john', 'last', 'smith', 'age', 32), 16 | Map.of('first', 'mary', 'last', 'wise', 'age', 56) 17 | ); 18 | 19 | // Using pick() and omit() as higher-order functions 20 | // to create callbacks for map() to filter specific 21 | // property names. 22 | const myPickedList = myList.map(pick('first', 'last')); 23 | const myOmittedList = myList.map(omit('first', 'last')); 24 | 25 | console.log('myList', myList.toJS()); 26 | // -> myList [ { first: 'joe', last: 'brown', age: 45 }, 27 | // -> { first: 'john', last: 'smith', age: 32 }, 28 | // -> { first: 'mary', last: 'wise', age: 56 } ] 29 | 30 | console.log('myPickedList', myPickedList.toJS()); 31 | // -> myPickedList [ { first: 'joe', last: 'brown' }, 32 | // -> { first: 'john', last: 'smith' }, 33 | // -> { first: 'mary', last: 'wise' } ] 34 | 35 | console.log('myOmittedList', myOmittedList.toJS()); 36 | // -> myOmittedList [ { age: 45 }, 37 | // -> { age: 32 }, 38 | // -> { age: 56 } ] 39 | -------------------------------------------------------------------------------- /07/05-reducing-max-min.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('red', 3, 'black', 4), 5 | Map.of('red', 6, 'black', 8), 6 | Map.of('red', 9, 'black', 12) 7 | ); 8 | 9 | // We can call reduce() directly to produce the minimum 10 | // and maximum values that we need. These are simple, 11 | // but a little redundant. The only difference between 12 | // the two is the operator. 13 | const minRedReduce = myList.reduce( 14 | (result, v) => v.get('red') < result ? 15 | v.get('red') : 16 | result, 17 | myList.first().get('red') 18 | ); 19 | const maxRedReduce = myList.reduce( 20 | (result, v) => v.get('red') > result ? 21 | v.get('red') : 22 | result, 23 | myList.first().get('red') 24 | ); 25 | 26 | // Another approach is to map the list to 27 | // all it's red values, then use the min() and 28 | // the max() functions to get the correponding 29 | // values. 30 | const minRedMap = myList 31 | .map(v => v.get('red')) 32 | .min(); 33 | const maxRedMap = myList 34 | .map(v => v.get('red')) 35 | .max(); 36 | 37 | // The minBy() and maxBy() functions combine the 38 | // map and reduce into one step. However, since this results 39 | // in the full object, we have to call get() if we only 40 | // want the number. 41 | const minByRed = myList 42 | .minBy(v => v.get('red')) 43 | .get('red'); 44 | const maxByRed = myList 45 | .maxBy(v => v.get('red')) 46 | .get('red'); 47 | 48 | console.log('minRedReduce', minRedReduce); 49 | // -> minRedReduce 3 50 | console.log('maxRedReduce', maxRedReduce); 51 | // -> maxRedReduce 9 52 | console.log('minRedMap', minRedMap); 53 | // -> minRedMap 3 54 | console.log('maxRedMap', maxRedMap); 55 | // -> maxRedMap 9 56 | console.log('minByRed', minByRed); 57 | // -> minByRed 3 58 | console.log('maxByRed', maxByRed); 59 | // -> maxByRed 9 60 | -------------------------------------------------------------------------------- /07/06-accumulating-values.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('red', 3, 'black', 4), 5 | Map.of('red', 6, 'black', 8), 6 | Map.of('red', 9, 'black', 12) 7 | ); 8 | 9 | // Reducing a list of maps to accumulate a specific 10 | // property is reasy. It can also be repetitive and 11 | // ineficient if we need more than one property. 12 | const redSum = myList.reduce( 13 | (result, v) => result + v.get('red'), 14 | 0 15 | ); 16 | const blackSum = myList.reduce( 17 | (result, v) => result + v.get('black'), 18 | 0 19 | ); 20 | 21 | // This approach reduces the list of maps to a single map, 22 | // instead of a single number. This avoids iterating the 23 | // entire list more than once. 24 | const groupedSums = myList 25 | .reduce((result, v) => result 26 | .update('red', r => r + v.get('red')) 27 | .update('black', b => b + v.get('black')) 28 | ); 29 | 30 | console.log('myList', myList.toJS()); 31 | // -> myList [ { red: 3, black: 4 }, 32 | // -> { red: 6, black: 8 }, 33 | // -> { red: 9, black: 12 } ] 34 | 35 | console.log('redSum', redSum); 36 | // -> redSum 18 37 | 38 | console.log('blackSum', blackSum); 39 | // -> blackSum 24 40 | 41 | console.log('groupedSums.red', groupedSums.get('red')); 42 | // -> groupedSums.red 18 43 | 44 | console.log('groupedSums.black', groupedSums.get('black')); 45 | // -> groupedSums.black 24 46 | -------------------------------------------------------------------------------- /07/07-multiple-map-calls.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Capitalizes the first letter of a given string. 4 | const capitalize = s => 5 | `${s.charAt(0).toUpperCase()}${s.slice(1)}`; 6 | 7 | const myList = List.of( 8 | Map.of('first', 'joe', 'last', 'brown', 'age', 45), 9 | Map.of('first', 'john', 'last', 'smith', 'age', 32), 10 | Map.of('first', 'mary', 'last', 'wise', 'age', 56) 11 | ); 12 | 13 | // Maps the list of maps to a list of strings. Instead of 14 | // doing everything in a single map() call, we create 15 | // three specialized map() calls that do one specific thing. 16 | // Since this is a sequence, splitting up map() calls has 17 | // no impact on performance. 18 | myList 19 | .toSeq() 20 | .map(v => v.update('first', capitalize)) 21 | .map(v => v.update('last', capitalize)) 22 | .map(v => [v.get('first'), v.get('last')].join(' ')) 23 | .forEach(v => console.log('name', v)); 24 | // -> name Joe Brown 25 | // -> name John Smith 26 | // -> name Mary Wise 27 | -------------------------------------------------------------------------------- /07/08-filtering-before-mapping.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Capitalizes the first letter of a given string. 4 | const capitalize = s => 5 | `${s.charAt(0).toUpperCase()}${s.slice(1)}`; 6 | 7 | const myList = List.of( 8 | Map.of('first', 'joe', 'last', 'brown', 'age', 45), 9 | Map.of('first', 'john', 'last', 'smith', 'age', 32), 10 | Map.of('first', 'mary', 'last', 'wise', 'age', 56) 11 | ); 12 | 13 | // Lazily filtering and mapping. When an item is found, 14 | // it is passed through each map() call before filtering 15 | // is continued. 16 | myList 17 | .toSeq() 18 | .filter(v => v.get('age') > 35) 19 | .map(v => v.update('first', capitalize)) 20 | .map(v => v.update('last', capitalize)) 21 | .map(v => [v.get('first'), v.get('last')].join(' ')) 22 | .forEach(v => console.log('name', v)); 23 | // -> name Joe Brown 24 | // -> name Mary Wise 25 | -------------------------------------------------------------------------------- /07/09-filter-map-reduce.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Capitalizes the first letter of a given string. 4 | const capitalize = s => 5 | `${s.charAt(0).toUpperCase()}${s.slice(1)}`; 6 | 7 | // Picks an array of of property values 8 | // from a map. 9 | const pick = (m, ...props) => 10 | props.map(p => m.get(p)); 11 | 12 | const myList = List.of( 13 | Map.of('first', 'joe', 'last', 'brown', 'age', 45), 14 | Map.of('first', 'john', 'last', 'smith', 'age', 32), 15 | Map.of('first', 'mary', 'last', 'wise', 'age', 56) 16 | ); 17 | 18 | console.log('myList', myList.toJS()); 19 | // -> myList [ { first: 'joe', last: 'brown', age: 45 }, 20 | // -> { first: 'john', last: 'smith', age: 32 }, 21 | // -> { first: 'mary', last: 'wise', age: 56 } ] 22 | 23 | // Filter, map reduce, side-effects. This is a common 24 | // pattern with immutable data. The best part is that 25 | // any given step is optional, 26 | myList 27 | .toSeq() 28 | .filter(v => v.get('age') < 50) 29 | .map(v => v.update('first', capitalize)) 30 | .map(v => v.update('last', capitalize)) 31 | .map(v => v.set('name', pick(v, 'first', 'last').join(' '))) 32 | .reduce( 33 | (result, v) => result 34 | .update('age', a => a + v.get('age')) 35 | .update('names', n => n.push(v.get('name'))), 36 | Map.of('names', List(), 'age', 0) 37 | ) 38 | .forEach( 39 | (v, k) => console.log(k, List.isList(v) ? v.toJS() : v) 40 | ); 41 | // -> names [ 'Joe Brown', 'John Smith' ] 42 | // -> age 77 43 | -------------------------------------------------------------------------------- /08/01-zipping-lists-of-simple-values.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const names = List.of('Jeremy', 'Garry', 'Katie'); 4 | const roles = List.of('Engineer', 'Designer', 'Programmer'); 5 | const ages = List.of(34, 23, 36); 6 | 7 | // Zips together the names, roles, and ages lists. 8 | // The argument passed to map is an array of items 9 | // with corresponding indexes from each list. 10 | names 11 | .zip(roles, ages) 12 | .map(v => v.join(', ')) 13 | .forEach(v => console.log('employee', v)); 14 | // -> employee Jeremy, Engineer, 34 15 | // -> employee Garry, Designer, 23 16 | // -> employee Katie, Programmer, 36 17 | -------------------------------------------------------------------------------- /08/02-zipping-lists-of-maps.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const names = List.of( 4 | Map.of('name', 'Jeremy'), 5 | Map.of('name', 'Garry'), 6 | Map.of('name', 'Katie') 7 | ); 8 | const roles = List.of( 9 | Map.of('role', 'Engineer'), 10 | Map.of('role', 'Designer'), 11 | Map.of('role', 'Programmer') 12 | ); 13 | const ages = List.of( 14 | Map.of('age', 34), 15 | Map.of('age', 23), 16 | Map.of('age', 36) 17 | ); 18 | 19 | // Each item in the lists that we're zipping together 20 | // is a map. This means that we can use zipWith() to 21 | // merge the maps together as they're zipped. 22 | names 23 | .zipWith( 24 | (...colls) => Map().merge(...colls), 25 | roles, 26 | ages 27 | ) 28 | .forEach(v => console.log('employee', v.toJS())); 29 | // -> employee { name: 'Jeremy', role: 'Engineer', age: 34 } 30 | // -> employee { name: 'Garry', role: 'Designer', age: 23 } 31 | // -> employee { name: 'Katie', role: 'Programmer', age: 36 } 32 | -------------------------------------------------------------------------------- /08/03-lazy-zipping.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const names = List.of( 4 | Map.of('name', 'Jeremy'), 5 | Map.of('name', 'Garry'), 6 | Map.of('name', 'Katie') 7 | ); 8 | const roles = List.of( 9 | Map.of('role', 'Engineer'), 10 | Map.of('role', 'Designer'), 11 | Map.of('role', 'Programmer') 12 | ); 13 | const ages = List.of( 14 | Map.of('age', 34), 15 | Map.of('age', 23), 16 | Map.of('age', 36) 17 | ); 18 | 19 | // When we call zipWith() on a sequence, the merges are 20 | // performed lazily. 21 | names 22 | .toSeq() 23 | .zipWith( 24 | (...maps) => Map().merge(...maps), 25 | roles, 26 | ages 27 | ) 28 | .forEach(v => console.log('lazy zipWith', v.toJS())); 29 | // -> lazy zipWith { name: 'Jeremy', role: 'Engineer', age: 34 } 30 | // -> lazy zipWith { name: 'Garry', role: 'Designer', age: 23 } 31 | // -> lazy zipWith { name: 'Katie', role: 'Programmer', age: 36 } 32 | 33 | // This works the same way as zipWith(), and everything 34 | // is evaluated lazily as well. It's just a different 35 | // approach to how the solution is structured. 36 | names 37 | .toSeq() 38 | .zip(roles, ages) 39 | .map(maps => Map().merge(...maps)) 40 | .forEach(v => console.log('lazy zip', v.toJS())); 41 | // -> lazy zip { name: 'Jeremy', role: 'Engineer', age: 34 } 42 | // -> lazy zip { name: 'Garry', role: 'Designer', age: 23 } 43 | // -> lazy zip { name: 'Katie', role: 'Programmer', age: 36 } 44 | -------------------------------------------------------------------------------- /08/04-deep-flattening-and-filtering.js: -------------------------------------------------------------------------------- 1 | import { fromJS } from 'immutable'; 2 | 3 | const myList = fromJS([ 4 | [1, 2], 5 | [3, 4], 6 | [ 7 | [5, 6], 8 | [ 9 | [7, 8], 10 | [9, 10], 11 | [ 12 | [11, 12], 13 | [13, 14], 14 | [ 15 | [15, 16], 16 | [17, 18], 17 | [ 18 | [19, 20] 19 | ] 20 | ] 21 | ] 22 | ] 23 | ] 24 | ]); 25 | 26 | console.log('myList', myList.flatten().toJS()); 27 | // -> myList [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 28 | // -> 11, 12, 13, 14, 15, 16, 17, 18, 29 | // -> 19, 20 ] 30 | 31 | // We're looking for odd numbers in a deeply-nested 32 | // list of numbers. If we convert the list to a sequence, 33 | // then flatten it, we can avoid recursion, and flatten 34 | // the list lazily. 35 | myList 36 | .toSeq() 37 | .flatten() 38 | .filter(v => v % 2) 39 | .take(5) 40 | .forEach(v => console.log('odd', v)); 41 | // -> odd 1 42 | // -> odd 3 43 | // -> odd 5 44 | // -> odd 7 45 | // -> odd 9 46 | -------------------------------------------------------------------------------- /08/05-shallow-flattening.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of( 4 | List.of('first', 'second'), 5 | List.of('third', 'fourth', List.of('fifth')), 6 | List.of('sixth', 'seventh') 7 | ); 8 | 9 | // Lists are recursed and flattened deeply by default. 10 | // This could be a performance hit, and have unintended 11 | // consequences. In this case, "fifth" is included even 12 | // though we don't want it. 13 | const myFlattenedList = myList.flatten(); 14 | 15 | // The true argument passed to flatten() tells it 16 | // to stay shallow. This way, the list that wraps 17 | // the "fifth" string isn't flattened, and it's 18 | // easy to filter out. 19 | const myShallowList = myList 20 | .flatten(true) 21 | .filterNot(List.isList); 22 | 23 | console.log('myList', myList.toJS()); 24 | // -> myList [ [ 'first', 'second' ], 25 | // -> [ 'third', 'fourth', [ 'fifth' ] ], 26 | // -> [ 'sixth', 'seventh' ] ] 27 | 28 | console.log('myFlattenedList', myFlattenedList.toJS()); 29 | // -> myFlattenedList [ 'first', 'second', 'third', 'fourth', 30 | // -> 'fifth', 'sixth', 'seventh' ] 31 | 32 | console.log('myShallowList', myShallowList.toJS()); 33 | // -> myShallowList [ 'first', 'second', 'third', 'fourth', 34 | // -> 'sixth', 'seventh' ] 35 | -------------------------------------------------------------------------------- /08/06-flattening-nested-maps.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of( 4 | 'first', 1, 5 | 'second', Map.of( 6 | 'third', 3, 7 | 'fourth', 4, 8 | 'fifth', Map.of( 9 | 'sixth', Map.of( 10 | 'seventh', 7 11 | ) 12 | ) 13 | ) 14 | ); 15 | 16 | console.log('myMap', myMap.toJS()); 17 | // -> { first: 1, 18 | // -> second: { 19 | // -> third: 3, 20 | // -> fourth: 4, 21 | // -> fifth: { 22 | // -> sixth: { seventh: 7 } } } } 23 | 24 | // When we flatten a map, we want a new map containing 25 | // only keys that have primitive values. In other words, 26 | // keys with Maps as values are recursively removed. 27 | myMap 28 | .toSeq() 29 | .flatten() 30 | .filter((v, k) => k.startsWith('f')) 31 | .forEach((v, k) => console.log(k, v)); 32 | // -> first 1 33 | // -> fourth 4 34 | -------------------------------------------------------------------------------- /09/01-strict-equality.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myMap = Map.of( 5 | 'one', 1, 6 | 'two', 2, 7 | 'three', 3 8 | ); 9 | 10 | // Modifies the list by pushing a new item. 11 | const myModifiedList = myList.push(4); 12 | // Modifies the map by setting a new key/value. 13 | const myModifiedMap = myMap.set('four', 4); 14 | // Returns the same instance of the list because 15 | // the first item is already 1. 16 | const myUnmodifiedList = myList.set(0, 1); 17 | // Returns the same instance of the map becuase 18 | // this key/value already exists. 19 | const myUnmodifiedMap = myMap.set('one', 1); 20 | 21 | console.log('myList', myList.toJS()); 22 | // -> myList [ 1, 2, 3 ] 23 | console.log('myMap', myMap.toJS()); 24 | // -> myMap { one: 1, two: 2, three: 3 } 25 | console.log('myModifiedList', myModifiedList.toJS()); 26 | // -> myModifiedList [ 1, 2, 3, 4 ] 27 | console.log('myModifiedMap', myModifiedMap.toJS()); 28 | // -> myModifiedMap { one: 1, two: 2, three: 3, four: 4 } 29 | console.log('myUnmodifiedList', myUnmodifiedList.toJS()); 30 | // -> myUnmodifiedList [ 1, 2, 3 ] 31 | console.log('myUnmodifiedMap', myUnmodifiedMap.toJS()); 32 | // -> myUnmodifiedMap { one: 1, two: 2, three: 3 } 33 | console.log( 34 | 'myList === myModifiedList', 35 | myList === myModifiedList 36 | ); 37 | // -> myList === myModifiedList false 38 | console.log( 39 | 'myMap === myModifiedMap', 40 | myMap === myModifiedMap 41 | ); 42 | // -> myMap === myModifiedMap false 43 | console.log( 44 | 'myList === myUnmodifiedList', 45 | myList === myUnmodifiedList 46 | ); 47 | // -> myList === myUnmodifiedList true 48 | console.log( 49 | 'myMap === myUnmodifiedMap', 50 | myMap === myUnmodifiedMap 51 | ); 52 | // -> myMap === myUnmodifiedMap true 53 | -------------------------------------------------------------------------------- /09/02-strict-equality-vs-equals.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap = Map.of('first', 1); 4 | const myModifiedMap = myMap.set('first', 'first'); 5 | const myUnmodifiedMap = myMap.set('first', 1); 6 | 7 | console.log('myMap', myMap.toJS()); 8 | // -> myMap { first: 1 } 9 | console.log('myModifiedMap', myModifiedMap.toJS()); 10 | // -> myModifiedMap { first: 'first' } 11 | console.log('myUnmodifiedMap', myUnmodifiedMap.toJS()); 12 | // -> myUnmodifiedMap { first: 1 } 13 | console.log( 14 | 'myMap === myModifiedMap', 15 | myMap === myModifiedMap 16 | ); 17 | // -> myMap === myModifiedMap false 18 | console.log( 19 | 'myMap === myUnmodifiedMap', 20 | myMap === myUnmodifiedMap 21 | ); 22 | // -> myMap === myUnmodifiedMap true 23 | console.log( 24 | 'myMap.equals(myModifiedMap)', 25 | myMap.equals(myModifiedMap) 26 | ); 27 | // -> myMap.equals(myModifiedMap) false 28 | console.log( 29 | 'myMap.equals(myUnmodifiedMap)', 30 | myMap.equals(myUnmodifiedMap) 31 | ); 32 | // -> myMap.equals(Map.of('first', 1)) true 33 | 34 | // Strict equality is much faster than using equals(), because 35 | // equals() has to do a lot more work. However, you can see 36 | // here that you have to be careful in cases where the 37 | // values are actually equal, yet strict equality returns 38 | // evaluates to false. 39 | console.log( 40 | 'myMap.equals(Map.of(\'first\', 1))', 41 | myMap.equals(Map.of('first', 1)) 42 | ); 43 | // -> myMap.equals(Map.of('first', 1)) true 44 | console.log( 45 | 'myMap === Map.of(\'first\', 1)', 46 | myMap === Map.of('first', 1) 47 | ); 48 | // -> myMap === Map.of('first', 1) false 49 | -------------------------------------------------------------------------------- /09/03-transformations-versus-mutations.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('one', 1, 'two', 2), 5 | Map.of('three', 3, 'four', 4), 6 | Map.of('five', 5, 'six', 6) 7 | ); 8 | 9 | // Transformation methods, like map(), never return the 10 | // same instance. Even in this case, where we're returning 11 | // the exact same map. 12 | const myTransformedList = myList.map(v => v); 13 | 14 | // Mutative methods, like update() and set(), will return 15 | // the same instance if nothing changes, as is the case here. 16 | const myMutatedList = myList 17 | .update(0, v => v.set('one', 1)); 18 | 19 | console.log('myList', myList.toJS()); 20 | // -> myList [ { one: 1, two: 2 }, 21 | // -> { three: 3, four: 4 }, 22 | // -> { five: 5, six: 6 } ] 23 | console.log('myTransformedList', myTransformedList.toJS()); 24 | // -> myTransformedList [ { one: 1, two: 2 }, 25 | // -> { three: 3, four: 4 }, 26 | // -> { five: 5, six: 6 } ] 27 | console.log('myMutatedList', myMutatedList.toJS()); 28 | // -> myMutatedList [ { one: 1, two: 2 }, 29 | // -> { three: 3, four: 4 }, 30 | // -> { five: 5, six: 6 } ] 31 | console.log( 32 | 'myList === myTransformedList', 33 | myList === myTransformedList 34 | ); 35 | // -> myList === myTransformedList false 36 | console.log( 37 | 'myList === myMutatedList', 38 | myList === myMutatedList 39 | ); 40 | // -> myList === myMutatedList true 41 | console.log( 42 | 'myList.equals(myTransformedList)', 43 | myList.equals(myTransformedList) 44 | ); 45 | // -> myList.equals(myTransformedList) true 46 | console.log( 47 | 'myList.equals(myMutatedList)', 48 | myList.equals(myMutatedList) 49 | ); 50 | // -> myList.equals(myMutatedList) true 51 | -------------------------------------------------------------------------------- /09/04-detecting-after-transfomations.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | 5 | // Transforms a list then iterates over it, logging 6 | // values. After we transform it, we check to see 7 | // if the result is the same as what we've 8 | // stored in prev. If the equals() method returns 9 | // true, then we can skip the side-effect. 10 | const mySideEffect = (list) => { 11 | const transformed = list 12 | .map(v => v * v); 13 | 14 | if (!transformed.equals(mySideEffect.prev)) { 15 | mySideEffect.prev = transformed; 16 | transformed.forEach( 17 | v => console.log('transformed', v) 18 | ); 19 | } 20 | }; 21 | 22 | console.log('first side-effect'); 23 | mySideEffect(myList); 24 | // -> first side-effect 25 | // -> transformed 1 26 | // -> transformed 4 27 | // -> transformed 9 28 | console.log('second side-effect'); 29 | mySideEffect(myList.set(0, 1)); 30 | // -> second side-effect 31 | console.log('third side-effect'); 32 | mySideEffect(myList.push(4)); 33 | // -> third side-effect 34 | // -> transformed 1 35 | // -> transformed 4 36 | // -> transformed 9 37 | // -> transformed 16 38 | console.log('fourth side-effect'); 39 | mySideEffect(myList.push(4)); 40 | // -> fourth side-effect 41 | -------------------------------------------------------------------------------- /09/05-detecting-before-transfomations.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3); 4 | const myModifiedList = myList.push(4); 5 | 6 | // Transforms a list then iterates over it, logging 7 | // values. After we transform it, we check to see 8 | // if the result is the same as what we've 9 | // stored in prev. If the equals() method returns 10 | // true, then we can skip the side-effect. 11 | const mySideEffect = (list) => { 12 | if (list !== mySideEffect.prev) { 13 | mySideEffect.prev = list; 14 | list 15 | .map(v => v * v) 16 | .forEach(v => console.log('transformed', v)); 17 | } 18 | }; 19 | 20 | console.log('first side-effect'); 21 | mySideEffect(myList); 22 | // -> first side-effect 23 | // -> transformed 1 24 | // -> transformed 4 25 | // -> transformed 9 26 | console.log('second side-effect'); 27 | mySideEffect(myList.set(0, 1)); 28 | // -> second side-effect 29 | console.log('third side-effect'); 30 | mySideEffect(myModifiedList); 31 | // -> third side-effect 32 | // -> transformed 1 33 | // -> transformed 4 34 | // -> transformed 9 35 | // -> transformed 16 36 | console.log('fourth side-effect'); 37 | mySideEffect(myModifiedList.set(0, 1)); 38 | // -> fourth side-effect 39 | -------------------------------------------------------------------------------- /09/06-caching-function-arguments.js: -------------------------------------------------------------------------------- 1 | import { List, Seq } from 'immutable'; 2 | 3 | // This is where cached argument values go. It's a WeakMap 4 | // because the functions that implement side effects are 5 | // the keys. When the function is garbage collected, we 6 | // want it's cache garbage collected too. 7 | const sideEffectCache = new WeakMap(); 8 | 9 | // Takes a function fn() and returns a new function that 10 | // caches argument values. When the function is called 11 | // again with the same argument values, nothing happens. 12 | // The idea is that nothing needs to happen since the 13 | // function implements side-effects. 14 | const sideEffect = fn => (...args) => { 15 | // Does the function have cached arguments? If not, 16 | // we'll initialize to an empty array so that we can 17 | // still zip() it with the args array. 18 | const cache = sideEffectCache.get(fn) 19 | || new Array(args.length); 20 | 21 | // This is true if no cached argument values are found. 22 | // Since the expectation is that each argument is an 23 | // Immutable.js type, we know that we can do a strict 24 | // comparison. So unless every argument value passes 25 | // the some() test, we consider it a miss and call 26 | // the side-effect. We also update the cached args. 27 | const miss = Seq(args) 28 | .zip(cache) 29 | .some(([a, b]) => a !== b); 30 | 31 | if (miss) { 32 | sideEffectCache.set(fn, args); 33 | fn(...args); 34 | } 35 | }; 36 | 37 | // We create side-effects by wrapping functions in sideEffect(). 38 | // Side effects are behavior that iterate over collections and 39 | // perform IO. 40 | const renderOddNumbers = sideEffect( 41 | coll => coll 42 | .toSeq() 43 | .filterNot(v => v % 2) 44 | .take(5) 45 | .forEach(v => console.log('odd', v)) 46 | ); 47 | 48 | // The same idea as renderOddNumbers(), except with two 49 | // parameters and a different side-effect. You so if we 50 | // get a cache hit, none of this code is executed - zip(), 51 | // map(), take(), and forEach(). If you're dealing with 52 | // a large collection, this is a big deal. 53 | const renderMultiples = sideEffect( 54 | (a, b) => a 55 | .toSeq() 56 | .zip(b) 57 | .map(([v1, v2]) => v1 * v2) 58 | .take(5) 59 | .forEach(v => console.log('multiple', v)) 60 | ); 61 | 62 | const myFirstList = List.of( 63 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 64 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 65 | ); 66 | 67 | const mySecondList = List.of( 68 | 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 69 | 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 70 | ); 71 | 72 | // Renders as expected, also populates the cache 73 | // for these two functions. 74 | console.log('first render'); 75 | renderOddNumbers(myFirstList); 76 | renderMultiples(myFirstList, mySecondList); 77 | // -> first render 78 | // -> odd 2 79 | // -> odd 4 80 | // -> odd 6 81 | // -> odd 8 82 | // -> odd 10 83 | // -> multiple 21 84 | // -> multiple 44 85 | // -> multiple 69 86 | // -> multiple 96 87 | // -> multiple 125 88 | 89 | // Looks like we're passing in different argument 90 | // values here, but, since nothing is actually changed 91 | // (index 0 is already 1), it's the same instance. 92 | // This means that nothing is rendered, because it's 93 | // already been rendered! 94 | console.log('second render'); 95 | renderOddNumbers(myFirstList.set(0, 1)); 96 | renderMultiples(myFirstList, mySecondList.set(0, 21)); 97 | // -> second render 98 | 99 | // This time we really are passing in different values. 100 | // The cache for these two functions is invalidated, and, 101 | // the side-effect is executed as expected. 102 | console.log('third render'); 103 | renderOddNumbers(myFirstList.insert(0, -1)); 104 | renderMultiples( 105 | myFirstList, 106 | mySecondList.push(100) 107 | ); 108 | // -> third render 109 | // -> odd 2 110 | // -> odd 4 111 | // -> odd 6 112 | // -> odd 8 113 | // -> odd 10 114 | // -> multiple 21 115 | // -> multiple 44 116 | // -> multiple 69 117 | // -> multiple 96 118 | // -> multiple 125 119 | -------------------------------------------------------------------------------- /10/01-not-the-same-as-lists.js: -------------------------------------------------------------------------------- 1 | import { List, Set } from 'immutable'; 2 | 3 | // In most ways, lists and maps are the same. 4 | const myList = List.of(1, 2, 3); 5 | const mySet = Set.of(1, 2, 3); 6 | 7 | console.log('myList', myList.toJS()); 8 | // -> myList [ 1, 2, 3 ] 9 | console.log('mySet', mySet.toJS()); 10 | // -> mySet [ 1, 2, 3 ] 11 | 12 | // Using get() with sets as though they're indexed 13 | // collections doesn't work. 14 | console.log('myList.get(0)', myList.get(0)); 15 | // -> myList.get(0) 1 16 | console.log('mySet.get(0)', mySet.get(0)); 17 | // -> mySet.get(0) undefined 18 | 19 | // Set iteration doesn't have a defined order, 20 | // so iterating over sets could have unexpected 21 | // results. 22 | console.log('myList.forEach()'); 23 | myList.forEach(v => console.log(v)); 24 | // -> myList.forEach() 25 | // -> 1 26 | // -> 2 27 | // -> 3 28 | console.log('mySet.forEach()'); 29 | mySet.forEach(v => console.log(v)); 30 | // -> mySet.forEach() 31 | // -> 1 32 | // -> 2 33 | // -> 3 34 | -------------------------------------------------------------------------------- /10/02-lists-to-sets-and-back.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | // A list with duplicates. 4 | const myList = List.of(1, 1, 2, 2, 3, 3); 5 | const mySet = myList.toSet(); 6 | 7 | // Converting a list to a set will remove duplicates. 8 | // You can then convert the list back to a list if needed. 9 | const myUniqueList = myList 10 | .toSet() 11 | .toList(); 12 | 13 | console.log('myList', myList.toJS()); 14 | // -> myList [ 1, 1, 2, 2, 3, 3 ] 15 | console.log('mySet', mySet.toJS()); 16 | // -> mySet [ 1, 2, 3 ] 17 | console.log('myUniqueList', myUniqueList.toJS()); 18 | // -> myUniqueList [ 1, 2, 3 ] 19 | -------------------------------------------------------------------------------- /10/03-lazy-duplicate-removal.js: -------------------------------------------------------------------------------- 1 | import { List, Map, Set, Repeat } from 'immutable'; 2 | 3 | const myList = List.of( 4 | Map.of('one', 1, 'two', 2), 5 | Map.of('three', 3, 'four', 4), 6 | Map.of('one', 1, 'two', 2), 7 | Map.of('five', 5, 'six', 6), 8 | Map.of('one', 1, 'two', 2) 9 | ); 10 | 11 | // This doesn't work, because there's no way to lazy 12 | // evaluate duplicates out of a list. 13 | myList 14 | .toSetSeq() 15 | .map(v => v.toJS()) 16 | .forEach(v => console.log('toSetSeq()', v)); 17 | // -> toSetSeq() { one: 1, two: 2 } 18 | // -> toSetSeq() { three: 3, four: 4 } 19 | // -> toSetSeq() { one: 1, two: 2 } 20 | // -> toSetSeq() { five: 5, six: 6 } 21 | // -> toSetSeq() { one: 1, two: 2 } 22 | 23 | 24 | // This works as expected, no more duplicates. 25 | myList 26 | .toSet() 27 | .toSeq() 28 | .map(v => v.toJS()) 29 | .forEach(v => console.log('toSet()', v)); 30 | // -> toSet() { one: 1, two: 2 } 31 | // -> toSet() { three: 3, four: 4 } 32 | // -> toSet() { five: 5, six: 6 } 33 | 34 | // We can lazily remove duplicates from lists 35 | // by keeping track of the values that have 36 | // already made it through. If we see it 37 | // again, we can ignore it. 38 | const lazySet = (list) => { 39 | let seen = Set(); 40 | 41 | return list 42 | .toSeq() 43 | .filter((v) => { 44 | if (seen.includes(v)) { 45 | return false; 46 | } 47 | 48 | seen = seen.add(v); 49 | return true; 50 | }); 51 | }; 52 | 53 | const myBigList = myList.concat( 54 | Repeat(Map.of('one', 1, 'two', 2), 10000) 55 | ); 56 | 57 | console.log('myBigList.size', myBigList.size); 58 | // -> myBigList.size 10005 59 | lazySet(myBigList) 60 | .map(v => v.toJS()) 61 | .take(2) 62 | .forEach(v => console.log('lazySet()', v)); 63 | // -> lazySet() { one: 1, two: 2 } 64 | // -> lazySet() { three: 3, four: 4 } 65 | -------------------------------------------------------------------------------- /10/04-sorting-ordered-sets.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of( 4 | 1, 3, 1, 2, 3, 4, 1, 5, 5 | 2, 4, 6, 1, 5, 2, 7, 1, 6 | 8, 3, 7, 1, 4, 2, 8, 9 7 | ); 8 | const myUniqueList = myList 9 | .toOrderedSet() 10 | .sort() 11 | .reverse() 12 | .toList(); 13 | 14 | console.log('myList', myList.toJS()); 15 | // -> myList [ 1, 3, 1, 2, 3, 4, 1, 5, 2, 4, 6, 16 | // -> 1, 5, 2, 7, 1, 8, 3, 7, 1, 4, 2, 17 | // -> 8, 9 ] 18 | console.log('myUniqueList', myUniqueList.toJS()); 19 | // -> myUniqueList [ 9, 8, 7, 6, 5, 20 | // -> 4, 3, 2, 1 ] 21 | -------------------------------------------------------------------------------- /10/05-iterating-ordered-sets.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of( 4 | 1, 3, 1, 2, 3, 4, 1, 5, 5 | 2, 4, 6, 1, 5, 2, 7, 1, 6 | 8, 3, 7, 1, 4, 2, 8, 9 7 | ); 8 | const myOrderedSet = myList 9 | .toOrderedSet() 10 | .sort() 11 | .reverse(); 12 | 13 | console.log('myList', myList.toJS()); 14 | // -> myList [ 1, 3, 1, 2, 3, 4, 1, 5, 2, 4, 6, 15 | // -> 1, 5, 2, 7, 1, 8, 3, 7, 1, 4, 2, 16 | // -> 8, 9 ] 17 | myOrderedSet.forEach(v => console.log('myOrderedSet', v)); 18 | // -> myOrderedSet 9 19 | // -> myOrderedSet 8 20 | // -> myOrderedSet 7 21 | // -> myOrderedSet 6 22 | // -> myOrderedSet 5 23 | // -> myOrderedSet 4 24 | // -> myOrderedSet 3 25 | // -> myOrderedSet 2 26 | // -> myOrderedSet 1 27 | -------------------------------------------------------------------------------- /10/06-adding-duplicates-to-sets.js: -------------------------------------------------------------------------------- 1 | import { OrderedSet, Map } from 'immutable'; 2 | 3 | // Prints the values of a set. The last property holds 4 | // the last set that was passed to this function. Since 5 | // adding a duplicate will always return a new set, even 6 | // though nothing changed, we have to use the more expensive 7 | // is() function to test for equality. 8 | const printValues = (set) => { 9 | if (!set.equals(printValues.prev)) { 10 | printValues.prev = set; 11 | set 12 | .valueSeq() 13 | .map(v => v.toJS()) 14 | .forEach(v => console.log(v)); 15 | } 16 | }; 17 | 18 | const myOrderedSet = OrderedSet.of( 19 | Map.of('one', 1), 20 | Map.of('two', 2) 21 | ); 22 | 23 | console.log('myOrderedSet'); 24 | printValues(myOrderedSet); 25 | // -> myOrderedSet 26 | // -> { one: 1 } 27 | // -> { two: 2 } 28 | 29 | console.log('adding 3'); 30 | printValues(myOrderedSet.add(Map.of('three', 3))); 31 | // -> adding 3 32 | // -> { one: 1 } 33 | // -> { two: 2 } 34 | // -> { three: 3 } 35 | 36 | console.log('adding 3 again'); 37 | printValues(myOrderedSet.add(Map.of('three', 3))); 38 | // -> adding 3 again 39 | -------------------------------------------------------------------------------- /11/01-set-intersections.js: -------------------------------------------------------------------------------- 1 | import { Set } from 'immutable'; 2 | 3 | const myFirstSet = Set.of(1, 2, 3, 4, 5, 6); 4 | const mySecondSet = Set.of(2, 4, 6, 8, 10); 5 | 6 | // Create a new set by intersecting with another. 7 | const myIntersection = myFirstSet.intersect(mySecondSet); 8 | 9 | // The resulting set intersection will always 10 | // be in the correct order since we're converting 11 | // it to an ordered set before intersecting. 12 | const myOrderedIntersection = myFirstSet 13 | .sort() 14 | .intersect(mySecondSet); 15 | 16 | console.log('myFirstSet', myFirstSet.toJS()); 17 | // -> myFirstSet [ 1, 2, 3, 4, 5, 6 ] 18 | console.log('mySecondSet', mySecondSet.toJS()); 19 | // -> mySecondSet [ 2, 4, 6, 8, 10 ] 20 | console.log('myIntersection', myIntersection.toJS()); 21 | // -> myIntersection [ 6, 2, 4 ] 22 | console.log('myOrderedIntersection', myOrderedIntersection.toJS()); 23 | // -> myOrderedIntersection [ 2, 4, 6 ] 24 | -------------------------------------------------------------------------------- /11/02-list-intersections.js: -------------------------------------------------------------------------------- 1 | import { List, Seq, Set } from 'immutable'; 2 | 3 | // Intersects the given lists, resulting in a new 4 | // list. The idea is to reduce the first list to 5 | // a new list that includes values that exist 6 | // in every other list. 7 | const intersect = (...lists) => { 8 | const [head] = lists; 9 | const tail = Seq(lists.slice(1)); 10 | 11 | return head.reduce((result, value) => 12 | tail 13 | .map(list => list.includes(value)) 14 | .includes(false) ? 15 | result : result.add(value), 16 | Set() 17 | ); 18 | }; 19 | 20 | const myList1 = List.of(1, 2, 3, 2); 21 | const myList2 = List.of(2, 3, 4); 22 | const myList3 = List.of(2, 3, 5); 23 | 24 | const myIntersection = intersect( 25 | myList1, 26 | myList2, 27 | myList3 28 | ); 29 | 30 | console.log('myList1', myList1.toJS()); 31 | // -> myList1 [ 1, 2, 3, 2 ] 32 | console.log('myList2', myList2.toJS()); 33 | // -> myList2 [ 2, 3, 4 ] 34 | console.log('myList3', myList3.toJS()); 35 | // -> myList3 [ 2, 3, 5 ] 36 | console.log('myIntersection', myIntersection.toJS()); 37 | // -> myIntersection [ 2, 3 ] 38 | -------------------------------------------------------------------------------- /11/03-set-differences.js: -------------------------------------------------------------------------------- 1 | import { Set } from 'immutable'; 2 | 3 | const mySet1 = Set.of('first', 'second', 'third'); 4 | const mySet2 = Set.of('first', 'second'); 5 | 6 | // You're taking whatever is in mySet2 out of 7 | // mySet1. Since "third" isn't in mySet2, it's 8 | // the difference. 9 | const myDiff1 = mySet1.subtract(mySet2); 10 | 11 | // Here, the order of the operation is changed, and 12 | // there's nothing left over after subtracting mySet1 13 | // from mySet2. This can be misleading since there's 14 | // obviously a difference between the two lists. 15 | const myDiff2 = mySet2.subtract(mySet1); 16 | 17 | console.log('mySet1', mySet1.toJS()); 18 | // -> mySet1 [ 'first', 'second', 'third' ] 19 | console.log('mySet2', mySet2.toJS()); 20 | // -> mySet2 [ 'first', 'second' ] 21 | console.log('myDiff1', myDiff1.toJS()); 22 | // -> myDiff1 [ 'third' ] 23 | console.log('myDiff2', myDiff2.toJS()); 24 | // -> myDiff2 [] 25 | -------------------------------------------------------------------------------- /11/04-list-differences.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // Takes an arbitrary number of lists, and finds the 4 | // difference between them. 5 | const difference = (...lists) => 6 | List() 7 | .concat(...lists) 8 | .countBy(v => v) 9 | .toSeq() 10 | .filter(v => v < lists.length) 11 | .keySeq(); 12 | 13 | // Works just like difference(), except it uses a different 14 | // filter(). 15 | const intersection = (...lists) => 16 | List() 17 | .concat(...lists) 18 | .countBy(v => v) 19 | .toSeq() 20 | .filter(v => v === lists.length) 21 | .keySeq(); 22 | 23 | const myList1 = List.of( 24 | Map.of('first', 1, 'second', 2), 25 | Map.of('third', 3, 'fourth', 4) 26 | ); 27 | 28 | const myList2 = List.of( 29 | Map.of('third', 3, 'fourth', 4), 30 | Map.of('fifth', 5, 'sixth', 6) 31 | ); 32 | 33 | const myList3 = List.of( 34 | Map.of('first', 1, 'second', 2), 35 | Map.of('third', 3, 'fourth', 4), 36 | Map.of('seventh', 7, 'eighth', 8) 37 | ); 38 | 39 | console.log('myList1', myList1.toJS()); 40 | // -> myList1 [ { first: 1, second: 2 }, { third: 3, fourth: 4 } ] 41 | console.log('myList2', myList2.toJS()); 42 | // -> myList2 [ { third: 3, fourth: 4 }, { fifth: 5, sixth: 6 } ] 43 | console.log('myList3', myList3.toJS()); 44 | // -> myList3 [ { first: 1, second: 2 }, 45 | // -> { third: 3, fourth: 4 }, 46 | // -> { seventh: 7, eighth: 8 } ] 47 | 48 | difference(myList1, myList2, myList3) 49 | .forEach((v) => { 50 | console.log('first diff', v.toJS()); 51 | }); 52 | // -> first diff { first: 1, second: 2 } 53 | // -> first diff { fifth: 5, sixth: 6 } 54 | // -> first diff { seventh: 7, eighth: 8 } 55 | 56 | difference(myList3, myList1, myList2) 57 | .forEach((v) => { 58 | console.log('second diff', v.toJS()); 59 | }); 60 | // -> second diff { first: 1, second: 2 } 61 | // -> second diff { seventh: 7, eighth: 8 } 62 | // -> second diff { fifth: 5, sixth: 6 } 63 | 64 | intersection(myList1, myList2, myList3) 65 | .forEach((v) => { 66 | console.log('intersection', v.toJS()); 67 | }); 68 | // -> intersection { third: 3, fourth: 4 } 69 | -------------------------------------------------------------------------------- /11/05-map-key-differences.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | // This works like diffing lists except that we have 4 | // to convert the maps into a list of key-value pair 5 | // lists. Then we can do our usual filter, then 6 | // convert them to a map. The end result is 7 | // a map with the key value pairs that aren't 8 | // in every map. 9 | const difference = (...maps) => 10 | Map(List() 11 | .concat(...maps.map(m => m.entrySeq())) 12 | .map(List) 13 | .countBy(v => v) 14 | .filter(v => v < maps.length) 15 | .keySeq()); 16 | 17 | // Intersection is the same as diffing except that 18 | // we want key-value pairs that exist in every map 19 | // argument. 20 | const intersection = (...maps) => 21 | Map(List() 22 | .concat(...maps.map(m => m.entrySeq())) 23 | .map(List) 24 | .countBy(v => v) 25 | .toSeq() 26 | .filter(v => v === maps.length) 27 | .keySeq()); 28 | 29 | const myMap1 = Map.of( 30 | 'one', 1, 'two', 2, 'three', 3 31 | ); 32 | 33 | const myMap2 = Map.of( 34 | 'one', 1, 'three', 3, 'four', 4 35 | ); 36 | 37 | const myMap3 = Map.of( 38 | 'one', 1, 'two', 2, 'five', 5 39 | ); 40 | 41 | console.log('difference'); 42 | difference(myMap1, myMap2, myMap3) 43 | .forEach((v, k) => console.log(k, v)); 44 | // -> difference 45 | // -> two 2 46 | // -> three 3 47 | // -> four 4 48 | // -> five 5 49 | 50 | console.log('intersection'); 51 | intersection(myMap1, myMap2, myMap3) 52 | .forEach((v, k) => console.log(k, v)); 53 | // -> intersection 54 | // -> one 1 55 | -------------------------------------------------------------------------------- /11/06-list-subsets.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of( 4 | List.of(1, 2, 3), 5 | List.of(4, 5, 6), 6 | List.of(7, 8, 9) 7 | ); 8 | 9 | const isSubset = List.of(1, 4, 7) 10 | .isSubset(myList.flatten()); 11 | 12 | const isSuperset = myList 13 | .flatten() 14 | .isSuperset(List.of(2, 5, 8)); 15 | 16 | console.log('isSubset', isSubset); 17 | // -> isSubset true 18 | console.log('isSuperset', isSuperset); 19 | // -> isSuperset true 20 | -------------------------------------------------------------------------------- /12/01-merging-maps-by-key.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap1 = Map.of( 4 | 'one', 1, 'two', 2, 'three', 3 5 | ); 6 | const myMap2 = Map.of( 7 | 'two', 22, 'three', 33, 'four', 4 8 | ); 9 | const myMergedMap = myMap1.merge(myMap2); 10 | const myMergedWithMap = myMap1.mergeWith( 11 | v => v, 12 | myMap2 13 | ); 14 | 15 | console.log('myMap1', myMap1.toJS()); 16 | // -> myMap1 { one: 1, two: 2, three: 3 } 17 | console.log('myMap2', myMap2.toJS()); 18 | // -> myMap2 { two: 22, three: 33, four: 4 } 19 | console.log('myMergedMap', myMergedMap.toJS()); 20 | // -> myMergedMap { one: 1, two: 22, three: 33, four: 4 } 21 | console.log('myMergedWithMap', myMergedWithMap.toJS()); 22 | // -> myMergedWithMap { one: 1, two: 2, three: 3, four: 4 } 23 | -------------------------------------------------------------------------------- /12/02-merging-maps-with-complex-keys.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myMap1 = Map.of( 4 | Map.of('name', 'one'), 1, 5 | Map.of('name', 'two'), 2, 6 | Map.of('name', 'three'), 3 7 | ); 8 | const myMap2 = Map.of( 9 | Map.of('name', 'two'), 22, 10 | Map.of('name', 'three'), 33, 11 | Map.of('name', 'four'), 4 12 | ); 13 | const myMergedMap = myMap1.merge(myMap2); 14 | const myMergedWith = myMap1.mergeWith( 15 | v => v, 16 | myMap2 17 | ); 18 | 19 | console.log('myMap1', myMap1.toJS()); 20 | // -> myMap1 { 'Map { "name": "one" }': 1, 21 | // -> 'Map { "name": "two" }': 2, 22 | // -> 'Map { "name": "three" }': 3 } 23 | console.log('myMap2', myMap2.toJS()); 24 | // -> myMap2 { 'Map { "name": "two" }': 22, 25 | // -> 'Map { "name": "three" }': 33, 26 | // -> 'Map { "name": "four" }': 4 } 27 | console.log('myMergedMap', myMergedMap.toJS()); 28 | // -> myMergedMap { 'Map { "name": "one" }': 1, 29 | // -> 'Map { "name": "two" }': 22, 30 | // -> 'Map { "name": "three" }': 33, 31 | // -> 'Map { "name": "four" }': 4 } 32 | console.log('myMergedWith', myMergedWith.toJS()); 33 | // -> myMergedWith { 'Map { "name": "one" }': 1, 34 | // -> 'Map { "name": "two" }': 2, 35 | // -> 'Map { "name": "three" }': 3, 36 | // -> 'Map { "name": "four" }': 4 } 37 | -------------------------------------------------------------------------------- /12/03-merging-lists-of-simple-values.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList1 = List.of(1, 2, 3); 4 | const myList2 = List.of(2, 3, 4, 5); 5 | const myMergedList = myList1.merge(myList2); 6 | const myMergedWithList = myList1.mergeWith( 7 | (a, b) => a === undefined ? b : a, 8 | myList2 9 | ); 10 | const myMergedReversed = myList2.mergeWith( 11 | (a, b) => a === undefined ? b : a, 12 | myList1 13 | ); 14 | 15 | 16 | console.log('myList1', myList1.toJS()); 17 | // -> myList1 [ 1, 2, 3 ] 18 | console.log('myList2', myList2.toJS()); 19 | // -> myList2 [ 2, 3, 4, 5 ] 20 | console.log('myMergedList', myMergedList.toJS()); 21 | // -> myMergedList [ 2, 3, 4, 5 ] 22 | console.log('myMergedWithList', myMergedWithList.toJS()); 23 | // -> myMergedWithList [ 1, 2, 3, 5 ] 24 | console.log('myMergedReversed', myMergedReversed.toJS()); 25 | // -> myMergedReversed [ 2, 3, 4, 5 ] 26 | -------------------------------------------------------------------------------- /12/04-merging-lists-of-maps.js: -------------------------------------------------------------------------------- 1 | import { List, Map } from 'immutable'; 2 | 3 | const myList1 = List.of( 4 | Map.of('one', 1, 'two', 2), 5 | Map.of('three', 3, 'four', 4) 6 | ); 7 | const myList2 = List.of( 8 | Map.of('one', 11, 'two', 22, 'three', 3), 9 | Map.of('four', 44, 'five', 5, 'size', 6) 10 | ); 11 | const myMergedList = myList1.merge(myList2); 12 | const myMergedWithList = myList1.mergeWith( 13 | (a, b) => a.mergeWith(v => v, b), 14 | myList2 15 | ); 16 | 17 | console.log('myList1', myList1.toJS()); 18 | // -> myList1 [ { one: 1, two: 2 }, { three: 3, four: 4 } ] 19 | console.log('myList2', myList2.toJS()); 20 | // -> myList2 [ { one: 11, two: 22, three: 3 }, 21 | // -> { four: 44, five: 5, size: 6 } ] 22 | console.log('myMergedList', myMergedList.toJS()); 23 | // -> myMergedList [ { one: 11, two: 22, three: 3 }, 24 | // -> { four: 44, five: 5, size: 6 } ] 25 | console.log('myMergedWithList', myMergedWithList.toJS()); 26 | // -> myMergedWithList [ { one: 1, two: 2, three: 3 }, 27 | // -> { three: 3, four: 4, five: 5, size: 6 } ] 28 | -------------------------------------------------------------------------------- /12/05-merging-lists-of-lists.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList1 = List.of( 4 | List.of(1, 2), 5 | List.of(3, 4) 6 | ); 7 | const myList2 = List.of( 8 | List.of(11, 21, 3), 9 | List.of(33, 44, 5) 10 | ); 11 | const myMergedList = myList1.merge(myList2); 12 | const myMergedWithList = myList1.mergeWith( 13 | (a, b) => a.mergeWith((a1, b1) => a1 === undefined ? b1 : a1, b), 14 | myList2 15 | ); 16 | 17 | console.log('myList1', myList1.toJS()); 18 | // -> myList1 [ [ 1, 2 ], [ 3, 4 ] ] 19 | console.log('myList2', myList2.toJS()); 20 | // -> myList2 [ [ 11, 21, 3 ], [ 33, 44, 5 ] ] 21 | console.log('myMergedList', myMergedList.toJS()); 22 | // -> myMergedList [ [ 11, 21, 3 ], [ 33, 44, 5 ] ] 23 | console.log('myMergedWithList', myMergedWithList.toJS()); 24 | // -> myMergedWithList [ [ 1, 2, 3 ], [ 3, 4, 5 ] ] 25 | -------------------------------------------------------------------------------- /12/06-concatenating-list-values.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList1 = List.of(1, 2, 3); 4 | const myList2 = List.of(4, 5, 6); 5 | const myList3 = List.of(7, 8, 9); 6 | const myCombinedList1 = myList1.concat( 7 | myList2, 8 | myList3 9 | ); 10 | 11 | console.log('myList1', myList1.toJS()); 12 | // -> myList1 [ 1, 2, 3 ] 13 | console.log('myList2', myList2.toJS()); 14 | // -> myList2 [ 4, 5, 6 ] 15 | console.log('myList3', myList3.toJS()); 16 | // -> myList3 [ 7, 8, 9 ] 17 | console.log('myCombinedList1', myCombinedList1.toJS()); 18 | // -> myCombinedList1 [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] 19 | -------------------------------------------------------------------------------- /12/07-lazy-concatenating-sequences.js: -------------------------------------------------------------------------------- 1 | import { Seq, Range } from 'immutable'; 2 | 3 | const mySeq1 = Range(1, 6) 4 | .filterNot((v) => { 5 | console.log('mySeq1', v); 6 | return v % 2; 7 | }); 8 | const mySeq2 = Range(6, 11) 9 | .filterNot((v) => { 10 | console.log('mySeq2', v); 11 | return v % 2; 12 | }); 13 | 14 | Seq() 15 | .concat(mySeq1, mySeq2) 16 | .forEach(v => console.log('result', v)); 17 | // -> mySeq1 1 18 | // -> mySeq1 2 19 | // -> result 2 20 | // -> mySeq1 3 21 | // -> mySeq1 4 22 | // -> result 4 23 | // -> mySeq1 5 24 | // -> mySeq2 6 25 | // -> result 6 26 | // -> mySeq2 7 27 | // -> mySeq2 8 28 | // -> result 8 29 | // -> mySeq2 9 30 | // -> mySeq2 10 31 | // -> result 10 32 | -------------------------------------------------------------------------------- /12/08-interposing-values.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList = List.of(1, 2, 3, 4, 5, 6, 7, 8); 4 | 5 | myList 6 | .toSeq() 7 | .filter((v) => { 8 | console.log('filtering', v); 9 | return v % 2; 10 | }) 11 | .interpose('...') 12 | .forEach(v => console.log(v)); 13 | // -> filtering 1 14 | // -> 1 15 | // -> filtering 2 16 | // -> filtering 3 17 | // -> ... 18 | // -> 3 19 | // -> filtering 4 20 | // -> filtering 5 21 | // -> ... 22 | // -> 5 23 | // -> filtering 6 24 | // -> filtering 7 25 | // -> ... 26 | // -> 7 27 | // -> filtering 8 28 | -------------------------------------------------------------------------------- /12/09-interleaving-values.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const myList1 = List.of(1, 2, 3); 4 | const myList2 = List.of(4, 5, 6); 5 | const myList3 = List.of(7, 8, 9); 6 | 7 | console.log('found', myList1 8 | .toSeq() 9 | .interleave(myList2, myList3) 10 | .find((v) => { 11 | console.log('checking', v); 12 | return v === 7; 13 | })); 14 | // -> checking 1 15 | // -> checking 4 16 | // -> checking 7 17 | // -> found 7 18 | -------------------------------------------------------------------------------- /13/01-maps-of-behavior.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const actions = Map.of( 4 | 'first', () => console.log('first'), 5 | 'second', () => console.log('second'), 6 | 'third', () => console.log('third') 7 | ); 8 | 9 | actions.get('second')(); 10 | // -> second 11 | actions.get('third')(); 12 | // -> third 13 | -------------------------------------------------------------------------------- /13/02-wrapping-behavior-maps-in-functions.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myBehavior = action => Map.of( 4 | 'first', () => console.log('first'), 5 | 'second', () => console.log('second'), 6 | 'third', () => console.log('third') 7 | ).get(action)(); 8 | 9 | myBehavior('first'); 10 | // -> first 11 | myBehavior('second'); 12 | // -> second 13 | myBehavior(); 14 | // -> TypeError: Map.of(...).get(...) is not a function 15 | -------------------------------------------------------------------------------- /13/03-providing-default-behavior.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const myBehavior = action => Map.of( 4 | true, () => console.log('thruth'), 5 | false, () => console.log('lies') 6 | ).get( 7 | action, 8 | () => console.log('default') 9 | )(); 10 | 11 | myBehavior(true); 12 | // -> truth 13 | myBehavior(false); 14 | // -> lies 15 | myBehavior(); 16 | // -> default 17 | myBehavior(123); 18 | // -> default 19 | -------------------------------------------------------------------------------- /13/04-parameterizing-behavior.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const log = value => console.log('log:', value); 4 | 5 | const myBehavior = (action, ...args) => Map.of( 6 | 'start', value => log(`starting ${value}...`), 7 | 'stop', value => log(`stopping ${value}...`) 8 | ).get( 9 | action, 10 | () => {} 11 | )(...args); 12 | 13 | myBehavior('start', 'database'); 14 | // -> log: starting database... 15 | myBehavior('start', 'server'); 16 | // -> log: starting server... 17 | myBehavior('stop', 'database'); 18 | // -> log: stopping database... 19 | myBehavior('stop', 'server'); 20 | // -> log: stopping server... 21 | -------------------------------------------------------------------------------- /13/05-composing-behavior.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const behavior = (behaviors, defaultBehavior = () => {}) => 4 | (action, ...args) => 5 | behaviors.get(action, defaultBehavior)(...args); 6 | 7 | const greaterThanFive = behavior(Map.of( 8 | true, n => `${n} is greater than 5`, 9 | false, n => `${n} is less than 5` 10 | )); 11 | 12 | const lessThanTen = behavior(Map.of( 13 | true, n => `${n} is less than 10`, 14 | false, n => `${n} is greater than 10` 15 | )); 16 | 17 | const a = 6; 18 | const b = 1; 19 | const c = 12; 20 | 21 | console.log(greaterThanFive(a > 5, a)); 22 | // -> 6 is greater than 5 23 | console.log(lessThanTen(a < 10, a)); 24 | // -> 6 is less than 10 25 | console.log(greaterThanFive(b > 5, b)); 26 | // -> 1 is less than 5 27 | console.log(lessThanTen(c < 10, c)); 28 | // -> 12 is greater than 10 29 | -------------------------------------------------------------------------------- /13/06-declarative-and-or-conditions.js: -------------------------------------------------------------------------------- 1 | import { Map, Seq } from 'immutable'; 2 | 3 | const behavior = (behaviors, defaultBehavior = () => {}) => 4 | (action, ...args) => 5 | behaviors.get(action, defaultBehavior)(...args); 6 | 7 | const some = (...predicates) => { 8 | const predicateSeq = Seq(predicates); 9 | return (...args) => predicateSeq.some(p => p(...args)); 10 | }; 11 | 12 | const every = (...predicates) => { 13 | const predicateSeq = Seq(predicates); 14 | return (...args) => predicateSeq.every(p => p(...args)); 15 | }; 16 | 17 | const hasEnough = some( 18 | v => v.get('source1') > 5, 19 | v => v.get('source2') > 5, 20 | v => v.get('source3') > 5 21 | ); 22 | 23 | const hasEverything = every( 24 | v => v.get('enabled'), 25 | v => v.get('hasPermission'), 26 | v => v.get('oldEnough') 27 | ); 28 | 29 | const hasBoth = every( 30 | hasEnough, 31 | hasEverything 32 | ); 33 | 34 | const logHasEnough = behavior(Map.of( 35 | true, () => console.log('yep, has enough'), 36 | false, () => console.log('sorry, not enough') 37 | )); 38 | 39 | const logHasEverything = behavior(Map.of( 40 | true, () => console.log('yep, has everything'), 41 | false, () => console.log('sorry, not everything') 42 | )); 43 | 44 | const logHasBoth = behavior(Map.of( 45 | true, () => console.log('yep, it has all of it'), 46 | false, () => console.log('nope') 47 | )); 48 | 49 | const myMap1 = Map.of( 50 | 'source1', 5, 51 | 'source2', 6, 52 | 'source3', 7 53 | ); 54 | const myMap2 = Map.of( 55 | 'source1', 6 56 | ); 57 | const myMap3 = Map.of( 58 | 'source1', 1, 59 | 'source2', 2, 60 | 'source3', 3 61 | ); 62 | const myMap4 = Map.of( 63 | 'enabled', true, 64 | 'hasPermission', true, 65 | 'oldEnough', true 66 | ); 67 | const myMap5 = Map.of( 68 | 'enabled', true, 69 | 'hasPermission', true 70 | ); 71 | const myMap6 = myMap1.merge(myMap4); 72 | const myMap7 = myMap1.merge(myMap5); 73 | 74 | console.log('myMap1', myMap1.toJS()); 75 | logHasEnough(hasEnough(myMap1)); 76 | // -> myMap1 { source1: 5, source2: 6, source3: 7 } 77 | // -> yep, has enough 78 | console.log('myMap2', myMap2.toJS()); 79 | logHasEnough(hasEnough(myMap2)); 80 | // -> myMap2 { source1: 6 } 81 | // -> yep, has enough 82 | console.log('myMap3', myMap3.toJS()); 83 | logHasEnough(hasEnough(myMap3)); 84 | // -> myMap3 { source1: 1, source2: 2, source3: 3 } 85 | // -> sorry, not enough 86 | console.log('myMap4', myMap4.toJS()); 87 | logHasEverything(hasEverything(myMap4)); 88 | // -> myMap4 { enabled: true, hasPermission: true, oldEnough: true } 89 | // -> yep, has everything 90 | console.log('myMap5', myMap5.toJS()); 91 | logHasEverything(hasEverything(myMap5)); 92 | // -> myMap5 { enabled: true, hasPermission: true } 93 | // -> sorry, not everything 94 | console.log('myMap6', myMap6.toJS()); 95 | logHasBoth(hasBoth(myMap6)); 96 | // -> myMap6 { source1: 5, source2: 6, 97 | // -> source3: 7, enabled: true, 98 | // -> hasPermission: true, oldEnough: true } 99 | // -> yep, it has all of it 100 | console.log('myMap7', myMap7.toJS()); 101 | logHasBoth(hasBoth(myMap7)); 102 | // -> myMap6 { source1: 5, source2: 6, 103 | // -> source3: 7, enabled: true, 104 | // -> hasPermission: true } 105 | // -> nope 106 | -------------------------------------------------------------------------------- /14/immutable-dom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |Date:
19 |Director:
20 |Rating:
21 |Date: {props.date}
109 |Director: {props.director}
110 |Rating: {props.rating}
111 |Date:
19 |Director:
20 |Rating:
21 |