├── 1-pure-functions ├── 01.js ├── 02.ts ├── 03.ts ├── 04.js ├── 05.js ├── 06.js ├── 07.js ├── 08.js ├── 09.js ├── 10.js ├── 11.js ├── 12.js └── 13.js ├── 2-immutability ├── 14.js ├── 15.js ├── 16.ts ├── 17.js ├── 18.js ├── 19.js ├── 20.js ├── 21.js ├── 22.js ├── 23.js ├── 24-solution.js └── 24.js ├── 3-composition ├── 25.ts ├── 26.ts ├── 27.ts ├── 28.ts ├── 29.js ├── 30.js ├── 31.js ├── 32.js ├── 33-solution.js ├── 33.js ├── 34-solution.js └── 34.js ├── 4-currying ├── 35.js ├── 36.js ├── 37.js ├── 38.ts ├── 39-solution.js ├── 39.js ├── 40.js ├── 41.js ├── 42-solution.js ├── 42.js ├── 43.js ├── 44-solution.js └── 44.js ├── 5-collections ├── 45.js ├── 46.js ├── 47.js ├── 48.js ├── 49.js ├── 50.js ├── 51.js ├── 52.js ├── 53.js ├── 54.js ├── 55.js ├── 56-solution.js ├── 56.js ├── 57.js ├── 58.js ├── 59.js ├── 60-solution.js ├── 60.js ├── 61-solution.js ├── 61.js ├── 62.js ├── 63-solution.js ├── 63.js ├── 64.js ├── 65-solution.js ├── 65.js ├── 66-solution.js └── 66.js ├── 6-async ├── 67.js ├── 68.js ├── 69.js ├── 70.js ├── 71.js ├── 72.js ├── 73.js ├── 74.js ├── 75-solution.js ├── 75.js ├── 76.js ├── 77.js ├── 78-solution.js ├── 78.js ├── 79.js ├── 80.js ├── 81.js ├── 82-solution.js └── 82.js ├── 7-io ├── 83.js ├── 84.js ├── 85.js ├── 86.js └── 87.js ├── AUTHOR ├── LICENSE └── README.md /1-pure-functions/01.js: -------------------------------------------------------------------------------- 1 | function greet(name) { 2 | console.log(`Welcome ${name}!`); 3 | } 4 | 5 | greet('Richard'); 6 | -------------------------------------------------------------------------------- /1-pure-functions/02.ts: -------------------------------------------------------------------------------- 1 | function greet(name: string): void { 2 | console.log(`Welcome ${name}!`); 3 | } 4 | 5 | greet('Richard'); 6 | -------------------------------------------------------------------------------- /1-pure-functions/03.ts: -------------------------------------------------------------------------------- 1 | function greet(name: string): string { 2 | return `Welcome ${name}!`; 3 | } 4 | 5 | console.log(greet('Richard')); 6 | -------------------------------------------------------------------------------- /1-pure-functions/04.js: -------------------------------------------------------------------------------- 1 | function greet(name) { 2 | sendAnalyticsEvent('Person was greeted'); 3 | return `Welcome ${name}!`; 4 | } 5 | 6 | console.log(greet('Richard')); 7 | -------------------------------------------------------------------------------- /1-pure-functions/05.js: -------------------------------------------------------------------------------- 1 | function greet(name) { 2 | return { 3 | type: 'console.log', 4 | payload: `Welcome ${name}!`, 5 | }; 6 | } 7 | 8 | var instruction = greet('Richard'); 9 | if (instruction.type === 'console.log') { 10 | console.log(instruction.payload); 11 | } 12 | -------------------------------------------------------------------------------- /1-pure-functions/06.js: -------------------------------------------------------------------------------- 1 | function greet(name) { 2 | return `console.log('Welcome ${name}!')`; 3 | } 4 | 5 | var instruction = greet('Richard'); 6 | eval(instruction); 7 | -------------------------------------------------------------------------------- /1-pure-functions/07.js: -------------------------------------------------------------------------------- 1 | 2 | function square(x) { 3 | return x * x; 4 | } 5 | -------------------------------------------------------------------------------- /1-pure-functions/08.js: -------------------------------------------------------------------------------- 1 | 2 | function setAge(user, age) { 3 | user.age = age; 4 | return user; 5 | } 6 | -------------------------------------------------------------------------------- /1-pure-functions/09.js: -------------------------------------------------------------------------------- 1 | 2 | function addAll(arrayNumbers) { 3 | var result = 0; 4 | for (var i = 0; i < arrayNumbers.length; i++) { 5 | result += arrayNumbers[i]; 6 | } 7 | return result; 8 | } 9 | 10 | console.log(addAll([10, 20, 30, 40])); 11 | -------------------------------------------------------------------------------- /1-pure-functions/10.js: -------------------------------------------------------------------------------- 1 | 2 | function foo(num) { 3 | return Math.random() * num; 4 | } 5 | -------------------------------------------------------------------------------- /1-pure-functions/11.js: -------------------------------------------------------------------------------- 1 | 2 | function times10(num) { 3 | if (1 === 2) { 4 | console.log(num * 10); 5 | } else { 6 | return num * 10; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /1-pure-functions/12.js: -------------------------------------------------------------------------------- 1 | 2 | function square(x) { 3 | x = x * x; 4 | return x; 5 | } 6 | -------------------------------------------------------------------------------- /1-pure-functions/13.js: -------------------------------------------------------------------------------- 1 | 2 | function add(x) { 3 | return function addX(y) { 4 | return x + y; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /2-immutability/14.js: -------------------------------------------------------------------------------- 1 | 2 | function setAge(user, age) { 3 | user.age = age; 4 | return user; 5 | } 6 | 7 | const userA = {age: 25, name: 'Richard'}; 8 | const userB = setAge(userA, 26); 9 | 10 | console.log(userA); 11 | console.log(userB); 12 | -------------------------------------------------------------------------------- /2-immutability/15.js: -------------------------------------------------------------------------------- 1 | 2 | function setAge(user, age) { 3 | var clonedUser = {age: user.age, name: user.name}; 4 | clonedUser.age = age; 5 | return clonedUser; 6 | } 7 | -------------------------------------------------------------------------------- /2-immutability/16.ts: -------------------------------------------------------------------------------- 1 | 2 | interface User { 3 | age: number; 4 | name: string; 5 | } 6 | 7 | function setAge(user: User, age: number): User { 8 | const clonedUser = { 9 | age: user.age, 10 | name: user.name 11 | }; 12 | clonedUser.age = age; 13 | return clonedUser; 14 | } 15 | 16 | const userA = {age: 25, name: 'Richard'}; 17 | const userB = setAge(userA, 26); 18 | 19 | console.log(userA); 20 | console.log(userB); 21 | -------------------------------------------------------------------------------- /2-immutability/17.js: -------------------------------------------------------------------------------- 1 | 2 | // pure function 3 | function addAll(arrayNumbers) { 4 | let result = 0; // mutable reference 5 | for (let i = 0; i < arrayNumbers.length; i++) { 6 | result = result + arrayNumbers[i]; 7 | } 8 | return result; 9 | } 10 | 11 | // impure function 12 | function setAge(user, age) { 13 | const resultUser = user; // immutable reference 14 | resultUser.age = age; // mutation of `user` 15 | return resultUser; 16 | } 17 | -------------------------------------------------------------------------------- /2-immutability/18.js: -------------------------------------------------------------------------------- 1 | 2 | function setAge(user, age) { 3 | var clonedUser = { 4 | age: user.age, 5 | firstname: user.firstname, 6 | lastname: user.lastname, 7 | address: user.address, 8 | dateBirth: user.dateBirth, 9 | placeBirth: user.placeBirth, 10 | username: user.username, 11 | }; 12 | clonedUser.age = age; 13 | return clonedUser; 14 | } 15 | -------------------------------------------------------------------------------- /2-immutability/19.js: -------------------------------------------------------------------------------- 1 | 2 | function setAge(user, age) { 3 | var clonedUser = Object.create(user); 4 | clonedUser.age = age; 5 | return clonedUser; 6 | } 7 | 8 | const userA = {age: 25, name: 'Richard'}; 9 | const userB = setAge(userA, 26); 10 | 11 | console.log(userA); 12 | console.log(userB); 13 | console.log(userB.name); 14 | console.log(userA.isPrototypeOf(userB)); 15 | -------------------------------------------------------------------------------- /2-immutability/20.js: -------------------------------------------------------------------------------- 1 | import Immutable from 'immutable'; 2 | 3 | function setAge(user, age) { 4 | return user.set('age', age); 5 | } 6 | 7 | const userA = Immutable.fromJS( 8 | {age: 25, name: 'Richard'} 9 | ); 10 | const userB = setAge(userA, 26); 11 | 12 | console.log(userA); 13 | console.log(userB); 14 | console.log(userA.toJS()); 15 | console.log(userB.toJS()); 16 | -------------------------------------------------------------------------------- /2-immutability/21.js: -------------------------------------------------------------------------------- 1 | 2 | function setThird(array, value) { 3 | array[2] = value; 4 | return array; 5 | } 6 | 7 | const array1 = [10, 20, null, 60]; 8 | const array2 = setThird(array1, 40); 9 | 10 | console.log(array1); 11 | console.log(array2); 12 | -------------------------------------------------------------------------------- /2-immutability/22.js: -------------------------------------------------------------------------------- 1 | 2 | function setThird(array, value) { 3 | const clonedArray = [...array]; 4 | // or: 5 | // const clonedArray = array.slice(); 6 | clonedArray[2] = value; 7 | return clonedArray; 8 | } 9 | 10 | const array1 = [10, 20, null, 60]; 11 | const array2 = setThird(array1, 40); 12 | 13 | console.log(array1); 14 | console.log(array2); 15 | -------------------------------------------------------------------------------- /2-immutability/23.js: -------------------------------------------------------------------------------- 1 | import Immutable from 'immutable'; 2 | 3 | function setThird(list, value) { 4 | return list.set(2, value); 5 | } 6 | 7 | const list1 = Immutable.fromJS([10, 20, null, 60]); 8 | const list2 = setThird(list1, 40); 9 | 10 | console.log(list1); 11 | console.log(list2); 12 | console.log(list1.toJS()); 13 | console.log(list2.toJS()); 14 | -------------------------------------------------------------------------------- /2-immutability/24-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 24.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | import Immutable from 'immutable'; 30 | 31 | function updateInvoiceTotal(invoice, shoppingCart) { 32 | let result = 0; 33 | const length = shoppingCart.get('products').size; 34 | for (let i = 0; i < length; i++) { 35 | result += shoppingCart.get('products').get(i).get('price'); 36 | } 37 | return invoice.set('total', result); 38 | } 39 | 40 | const invoice = Immutable.fromJS({ 41 | total: 2055, 42 | }); 43 | 44 | console.log(`Invoice before: ${invoice.get('total')}`); 45 | 46 | const shoppingCart = Immutable.fromJS({ 47 | products: [ 48 | {price: 150}, 49 | {price: 502}, 50 | {price: 97}, 51 | {price: 818}, 52 | {price: 620} 53 | ], 54 | }); 55 | 56 | const newInvoice = updateInvoiceTotal(invoice, shoppingCart); 57 | 58 | console.log(`Invoice after: ${invoice.get('total')}`); 59 | 60 | console.log(`New invoice: ${newInvoice.get('total')}`); 61 | -------------------------------------------------------------------------------- /2-immutability/24.js: -------------------------------------------------------------------------------- 1 | import Immutable from 'immutable'; 2 | 3 | function updateInvoiceTotal(invoice, shoppingCart) { 4 | let result = 0; 5 | const length = shoppingCart.products.length; 6 | for (let i = 0; i < length; i++) { 7 | result += shoppingCart.products[i].price; 8 | } 9 | invoice.total = result; 10 | } 11 | 12 | const invoice = { 13 | total: 2055, 14 | }; 15 | 16 | console.log(`Invoice before: ${invoice.total}`); 17 | 18 | const shoppingCart = { 19 | products: [ 20 | {price: 150}, 21 | {price: 502}, 22 | {price: 97}, 23 | {price: 818}, 24 | {price: 620} 25 | ], 26 | }; 27 | 28 | updateInvoiceTotal(invoice, shoppingCart); 29 | 30 | console.log(`Invoice after: ${invoice.total}`); 31 | -------------------------------------------------------------------------------- /3-composition/25.ts: -------------------------------------------------------------------------------- 1 | function greet(name: string, female: boolean): string { 2 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 3 | } 4 | 5 | console.log(greet('Richard', false)); 6 | -------------------------------------------------------------------------------- /3-composition/26.ts: -------------------------------------------------------------------------------- 1 | function maleify(name: string): string { 2 | return `Mr. ${name}`; 3 | } 4 | 5 | function femaleify(name: string): string { 6 | return `Ms. ${name}`; 7 | } 8 | 9 | function greet(person: string): string { 10 | return `Hi ${person}`; 11 | } 12 | 13 | console.log(greet(femaleify('Lisa'))); 14 | -------------------------------------------------------------------------------- /3-composition/27.ts: -------------------------------------------------------------------------------- 1 | function maleify(name: string): string { 2 | return `Mr. ${name}`; 3 | } 4 | 5 | function femaleify(name: string): string { 6 | return `Ms. ${name}`; 7 | } 8 | 9 | function greet(person: string): string { 10 | return `Hi ${person}`; 11 | } 12 | 13 | function greetFemale(name: string): string { 14 | return greet(femaleify(name)); 15 | } 16 | 17 | console.log(greetFemale('Lisa')); 18 | -------------------------------------------------------------------------------- /3-composition/28.ts: -------------------------------------------------------------------------------- 1 | function maleify(name: string): string { 2 | return `Mr. ${name}`; 3 | } 4 | 5 | function femaleify(name: string): string { 6 | return `Ms. ${name}`; 7 | } 8 | 9 | function greet(person: string): string { 10 | return `Hi ${person}`; 11 | } 12 | 13 | function composeLTR(fn1: Function, fn2: Function): Function { 14 | return function (x) { return fn2(fn1(x)); }; 15 | } 16 | 17 | const greetFemale = composeLTR(femaleify, greet); 18 | 19 | console.log(greetFemale('Lisa')); 20 | -------------------------------------------------------------------------------- /3-composition/29.js: -------------------------------------------------------------------------------- 1 | const maleify = (name) => `Mr. ${name}`; 2 | const femaleify = (name) => `Ms. ${name}`; 3 | const greet = (person) => `Hi ${person}`; 4 | 5 | const composeLTR = (fn1, fn2) => 6 | function (x) { return fn2(fn1(x)); }; 7 | 8 | const greetFemale = composeLTR(femaleify, greet); 9 | 10 | console.log(greetFemale('Lisa')); 11 | -------------------------------------------------------------------------------- /3-composition/30.js: -------------------------------------------------------------------------------- 1 | const maleify = (name) => `Mr. ${name}`; 2 | const femaleify = (name) => `Ms. ${name}`; 3 | const greet = (person) => `Hi ${person}`; 4 | 5 | const composeLTR = (fn1, fn2) => x => fn2(fn1(x)); 6 | const composeRTL = (fn1, fn2) => x => fn1(fn2(x)); 7 | 8 | const greetFemale = composeLTR(femaleify, greet); 9 | const weirdGreet = composeRTL(femaleify, greet); 10 | 11 | console.log(greetFemale('Lisa')); 12 | console.log(weirdGreet('Lisa')); 13 | -------------------------------------------------------------------------------- /3-composition/31.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | const maleify = (name) => `Mr. ${name}`; 4 | const femaleify = (name) => `Ms. ${name}`; 5 | const greet = (person) => `Hi ${person}`; 6 | 7 | // const composeLTR = _.pipe; 8 | // const composeRTL = _.compose; 9 | 10 | const greetFemale = _.pipe(femaleify, greet); 11 | const weirdGreet = _.compose(femaleify, greet); 12 | 13 | console.log(greetFemale('Lisa')); 14 | console.log(weirdGreet('Lisa')); 15 | -------------------------------------------------------------------------------- /3-composition/32.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | const maleify = (name) => `Mr. ${name}`; 4 | const femaleify = _.memoize(function (name) { 5 | console.log(`(Computing the female title for ${name}...)`); 6 | return `Ms. ${name}`; 7 | }); 8 | const greet = (person) => `Hi ${person}`; 9 | const formalGreet = (person) => `Welcome ${person}.`; 10 | 11 | const greetFemale = _.pipe(femaleify, greet); 12 | const formalGreetFemale = _.pipe(femaleify, greet); 13 | 14 | console.log(greetFemale('Lisa')); 15 | console.log(formalGreetFemale('Lisa')); 16 | -------------------------------------------------------------------------------- /3-composition/33-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 33.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | import _ from 'lodash/fp'; 30 | import Immutable from 'immutable'; 31 | 32 | const setUserProperty = (property) => function (user, value) { 33 | return user.set(property, value); 34 | }; 35 | const setAge = setUserProperty('age'); 36 | 37 | const userA = Immutable.fromJS( 38 | {age: 25, name: 'Richard'} 39 | ); 40 | const userB = setAge(userA, 26); 41 | 42 | console.log(userA); 43 | console.log(userB); 44 | console.log(userA.toJS()); 45 | console.log(userB.toJS()); 46 | -------------------------------------------------------------------------------- /3-composition/33.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | const setUserProperty = (property) => function (user, value) { 5 | // write this function 6 | }; 7 | const setAge = // build this function from setUserProperty 8 | 9 | const userA = Immutable.fromJS( 10 | {age: 25, name: 'Richard'} 11 | ); 12 | const userB = setAge(userA, 26); 13 | 14 | console.log(userA); 15 | console.log(userB); 16 | console.log(userA.toJS()); 17 | console.log(userB.toJS()); 18 | -------------------------------------------------------------------------------- /3-composition/34-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 34.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | import _ from 'lodash/fp'; 30 | import Immutable from 'immutable'; 31 | 32 | function increaseFirstProductPrice(shoppingCart) { 33 | return shoppingCart.update('products', products => 34 | products.update(0, product => 35 | product.update('price', price => 36 | price + 100 37 | ) 38 | ) 39 | ); 40 | } 41 | 42 | function totalInShoppingCart(shoppingCart) { 43 | let result = 0; 44 | const length = shoppingCart.get('products').size; 45 | for (let i = 0; i < length; i++) { 46 | result += shoppingCart.get('products').get(i).get('price'); 47 | } 48 | return result; 49 | } 50 | 51 | function updateInvoiceTotal(invoice) { 52 | return function (newTotal) { 53 | return invoice.set('total', newTotal); 54 | }; 55 | } 56 | 57 | function discount(invoice) { 58 | return invoice.update('total', total => total * 0.9); 59 | } 60 | 61 | const invoice = Immutable.fromJS({ 62 | total: 2055, 63 | }); 64 | console.log(`Invoice before: ${invoice.get('total')}`); 65 | 66 | const calculateTotal = _.pipe( 67 | increaseFirstProductPrice, 68 | totalInShoppingCart, 69 | updateInvoiceTotal(invoice), 70 | discount 71 | ); 72 | 73 | const shoppingCart = Immutable.fromJS({ 74 | products: [ 75 | {price: 150}, 76 | {price: 502}, 77 | {price: 97}, 78 | {price: 818}, 79 | {price: 620} 80 | ], 81 | }); 82 | 83 | const newInvoice = calculateTotal(shoppingCart); 84 | 85 | console.log(`Invoice after: ${invoice.get('total')}`); 86 | console.log(`New invoice: ${newInvoice.get('total')}`); 87 | // The console output should report: 88 | // > New invoice: 2058.3 89 | -------------------------------------------------------------------------------- /3-composition/34.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | function increaseFirstProductPrice(shoppingCart) { 5 | // return a new shoppingCart immutable structure 6 | // where the price of the first product is updated 7 | // with +100 more than before. 8 | } 9 | 10 | function updateInvoiceTotal(invoice, shoppingCart) { 11 | // You maybe need to change/fix this function. 12 | let result = 0; 13 | const length = shoppingCart.get('products').size; 14 | for (let i = 0; i < length; i++) { 15 | result += shoppingCart.get('products').get(i).get('price'); 16 | } 17 | return invoice.set('total', result); 18 | } 19 | 20 | function discount(invoice) { 21 | // return a new invoice immutable structure 22 | // where the total field is updated to 90% 23 | // of the previous value. 24 | } 25 | 26 | const invoice = Immutable.fromJS({ 27 | total: 2055, 28 | }); 29 | console.log(`Invoice before: ${invoice.get('total')}`); 30 | 31 | // Create a function called `calculateTotal` 32 | // that calls, in sequence, 33 | // 1. increaseFirstProductPrice 34 | // 2. updateInvoiceTotal 35 | // 3. discount 36 | // You may need to change something else to make it work. 37 | const calculateTotal = // ... 38 | 39 | const shoppingCart = Immutable.fromJS({ 40 | products: [ 41 | {price: 150}, 42 | {price: 502}, 43 | {price: 97}, 44 | {price: 818}, 45 | {price: 620} 46 | ], 47 | }); 48 | 49 | const newInvoice = // call calculateTotal here 50 | 51 | console.log(`Invoice after: ${invoice.get('total')}`); 52 | console.log(`New invoice: ${newInvoice.get('total')}`); 53 | -------------------------------------------------------------------------------- /4-currying/35.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | function increaseFirstProductPrice(shoppingCart) { 5 | return shoppingCart.update('products', products => 6 | products.update(0, product => 7 | product.update('price', price => 8 | price + 100 9 | ) 10 | ) 11 | ); 12 | } 13 | 14 | const updateInvoiceTotal = _.curry( 15 | function (invoice, shoppingCart) { 16 | let result = 0; 17 | const length = shoppingCart.get('products').size; 18 | for (let i = 0; i < length; i++) { 19 | result += shoppingCart.get('products').get(i).get('price'); 20 | } 21 | return invoice.set('total', result); 22 | } 23 | ); 24 | 25 | function discount(invoice) { 26 | return invoice.update('total', total => total * 0.9); 27 | } 28 | 29 | const invoice = Immutable.fromJS({ 30 | total: 2055, 31 | }); 32 | console.log(`Invoice before: ${invoice.get('total')}`); 33 | 34 | const calculateTotal = _.pipe( 35 | increaseFirstProductPrice, 36 | updateInvoiceTotal(invoice), 37 | discount 38 | ); 39 | 40 | const shoppingCart = Immutable.fromJS({ 41 | products: [ 42 | {price: 150}, 43 | {price: 502}, 44 | {price: 97}, 45 | {price: 818}, 46 | {price: 620} 47 | ], 48 | }); 49 | 50 | const newInvoice = calculateTotal(shoppingCart); 51 | 52 | console.log(`Invoice after: ${invoice.get('total')}`); 53 | console.log(`New invoice: ${newInvoice.get('total')}`); 54 | -------------------------------------------------------------------------------- /4-currying/36.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | function increaseFirstProductPrice(shoppingCart) { 5 | return shoppingCart.update('products', products => 6 | products.update(0, product => 7 | product.update('price', price => 8 | price + 100 9 | ) 10 | ) 11 | ); 12 | } 13 | 14 | const updateInvoiceTotal = (invoice) => (shoppingCart) => { 15 | let result = 0; 16 | const length = shoppingCart.get('products').size; 17 | for (let i = 0; i < length; i++) { 18 | result += shoppingCart.get('products').get(i).get('price'); 19 | } 20 | return invoice.set('total', result); 21 | }; 22 | 23 | function discount(invoice) { 24 | return invoice.update('total', total => total * 0.9); 25 | } 26 | 27 | const invoice = Immutable.fromJS({ 28 | total: 2055, 29 | }); 30 | console.log(`Invoice before: ${invoice.get('total')}`); 31 | 32 | const calculateTotal = _.pipe( 33 | increaseFirstProductPrice, 34 | updateInvoiceTotal(invoice), 35 | discount 36 | ); 37 | 38 | const shoppingCart = Immutable.fromJS({ 39 | products: [ 40 | {price: 150}, 41 | {price: 502}, 42 | {price: 97}, 43 | {price: 818}, 44 | {price: 620} 45 | ], 46 | }); 47 | 48 | const newInvoice = calculateTotal(shoppingCart); 49 | 50 | console.log(`Invoice after: ${invoice.get('total')}`); 51 | console.log(`New invoice: ${newInvoice.get('total')}`); 52 | -------------------------------------------------------------------------------- /4-currying/37.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | function increaseFirstProductPrice(shoppingCart) { 5 | return shoppingCart.update('products', products => 6 | products.update(0, product => 7 | product.update('price', price => 8 | price + 100 9 | ) 10 | ) 11 | ); 12 | } 13 | 14 | function makeUpdateInvoiceTotal(invoice) { 15 | return function updateInvoiceTotal(shoppingCart) { 16 | let result = 0; 17 | const length = shoppingCart.get('products').size; 18 | for (let i = 0; i < length; i++) { 19 | result += shoppingCart.get('products').get(i).get('price'); 20 | } 21 | return invoice.set('total', result); 22 | }; 23 | } 24 | 25 | function discount(invoice) { 26 | return invoice.update('total', total => total * 0.9); 27 | } 28 | 29 | const invoice = Immutable.fromJS({ 30 | total: 2055, 31 | }); 32 | console.log(`Invoice before: ${invoice.get('total')}`); 33 | 34 | const calculateTotal = _.pipe( 35 | increaseFirstProductPrice, 36 | makeUpdateInvoiceTotal(invoice), 37 | discount 38 | ); 39 | 40 | const shoppingCart = Immutable.fromJS({ 41 | products: [ 42 | {price: 150}, 43 | {price: 502}, 44 | {price: 97}, 45 | {price: 818}, 46 | {price: 620} 47 | ], 48 | }); 49 | 50 | const newInvoice = calculateTotal(shoppingCart); 51 | 52 | console.log(`Invoice after: ${invoice.get('total')}`); 53 | console.log(`New invoice: ${newInvoice.get('total')}`); 54 | -------------------------------------------------------------------------------- /4-currying/38.ts: -------------------------------------------------------------------------------- 1 | function basicAdd(x: number, y: number): number { 2 | return x + y; 3 | } 4 | 5 | function add(x: number): ((y: number) => number) { 6 | return function addX(y: number): number { 7 | return basicAdd(x, y); 8 | }; 9 | } 10 | 11 | // or: 12 | // const add = _.curry(basicAdd); 13 | 14 | const addTen = add(10); 15 | 16 | console.log(addTen(4)); // 14 17 | console.log(add(10)(4)); // 14 18 | -------------------------------------------------------------------------------- /4-currying/39-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 39.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | import _ from 'lodash/fp'; 30 | 31 | function greet(female, name) { 32 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 33 | } 34 | 35 | const greetFemale = _.curry(greet)(true); 36 | 37 | console.log(greetFemale('Lisa')); 38 | -------------------------------------------------------------------------------- /4-currying/39.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | function greet(name, female) { 4 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 5 | } 6 | 7 | // make this function by reusing `greet` with currying 8 | // You may need to change/fix somethings elsewhere. 9 | const greetFemale = // ? 10 | 11 | console.log(greetFemale('Lisa')); 12 | -------------------------------------------------------------------------------- /4-currying/40.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | function greet(female, name) { 4 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 5 | } 6 | 7 | const greetFemale = _.curry(greet)(true); 8 | 9 | function greetLisa() { 10 | return greetFemale('Lisa'); 11 | } 12 | 13 | const LISA_GREETING = greetFemale('Lisa'); 14 | 15 | console.log(greetLisa()); 16 | console.log(LISA_GREETING); 17 | -------------------------------------------------------------------------------- /4-currying/41.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | function greet(female, name) { 4 | console.log('running greet()...'); 5 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 6 | } 7 | 8 | const greetFemale = _.curry(greet)(true); 9 | 10 | function greetLisa() { 11 | return greetFemale('Lisa'); 12 | } 13 | 14 | const LISA_GREETING = greetFemale('Lisa'); 15 | 16 | console.log('BEFORE'); 17 | console.log(greetLisa()); 18 | console.log(LISA_GREETING); 19 | console.log('AFTER'); 20 | -------------------------------------------------------------------------------- /4-currying/42-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 42.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | function greet(female, name) { 30 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 31 | } 32 | 33 | function thunkify(fn, ...args) { 34 | return () => fn(...args); 35 | } 36 | 37 | const greetLisa = thunkify(greet, true, 'Lisa'); 38 | 39 | console.log(greetLisa()); 40 | -------------------------------------------------------------------------------- /4-currying/42.js: -------------------------------------------------------------------------------- 1 | function greet(female, name) { 2 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 3 | } 4 | 5 | function thunkify(fn, ...args) { 6 | // write this function to return a thunk that runs fn 7 | } 8 | 9 | const greetLisa = // use `thunkify` and `greet` to make this thunk 10 | 11 | console.log(greetLisa()); 12 | -------------------------------------------------------------------------------- /4-currying/43.js: -------------------------------------------------------------------------------- 1 | // Curried function 2 | function makeGreet(female) { 3 | return function greet(name) { 4 | return `Hi ${female ? 'Ms.' : 'Mr.'} ${name}!`; 5 | }; 6 | } 7 | 8 | // VERSUS 9 | 10 | // Class 11 | class Greeter { 12 | constructor(female) { 13 | this.female = female; 14 | } 15 | 16 | greet(name) { 17 | return `Hi ${this.female ? 'Ms.' : 'Mr.'} ${name}!`; 18 | } 19 | } 20 | 21 | console.log(makeGreet(true)('Lisa')); 22 | console.log((new Greeter(true)).greet('Lisa')); 23 | -------------------------------------------------------------------------------- /4-currying/44-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 44.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | class Rectangle { 30 | constructor(height, width) { 31 | this.height = height; 32 | this.width = width; 33 | } 34 | 35 | getArea() { 36 | return this.calcArea(); 37 | } 38 | 39 | calcArea() { 40 | return this.height * this.width; 41 | } 42 | } 43 | 44 | // Solution: 45 | 46 | function rectangleArea(height) { 47 | return function rectangleAreaGivenHeight(width) { 48 | const calcArea = () => height * width; 49 | return function getArea() { 50 | return calcArea(); 51 | }; 52 | } 53 | } 54 | 55 | const getArea = rectangleArea(3)(4); 56 | console.log(getArea()); 57 | -------------------------------------------------------------------------------- /4-currying/44.js: -------------------------------------------------------------------------------- 1 | // Given this class... 2 | class Rectangle { 3 | constructor(height, width) { 4 | this.height = height; 5 | this.width = width; 6 | } 7 | 8 | getArea() { 9 | return this.calcArea(); 10 | } 11 | 12 | calcArea() { 13 | return this.height * this.width; 14 | } 15 | } 16 | 17 | const rect = new Rectangle(3, 4); 18 | console.log(rect.getArea()); 19 | 20 | // Create a function with closures that achieves the same goals. 21 | // No multiple-argument functions allowed! 22 | // Essentially, we want to get rid of classes and `this`. 23 | -------------------------------------------------------------------------------- /5-collections/45.js: -------------------------------------------------------------------------------- 1 | function updateInvoiceTotal(invoice, shoppingCart) { 2 | let result = 0; 3 | const length = shoppingCart.products.length; 4 | for (let i = 0; i < length; i++) { 5 | result += shoppingCart.products[i].price; 6 | } 7 | invoice.total = result; 8 | } 9 | 10 | const invoice = { 11 | total: 2055, 12 | }; 13 | 14 | console.log(`Invoice before: ${invoice.total}`); 15 | 16 | const shoppingCart = { 17 | products: [ 18 | {price: 150}, 19 | {price: 502}, 20 | {price: 97}, 21 | {price: 818}, 22 | {price: 620} 23 | ], 24 | }; 25 | 26 | updateInvoiceTotal(invoice, shoppingCart); 27 | 28 | console.log(`Invoice after: ${invoice.total}`); 29 | -------------------------------------------------------------------------------- /5-collections/46.js: -------------------------------------------------------------------------------- 1 | function getPriceArray(shoppingCart) { 2 | // return an array of numbers 3 | // [150, 502, 97, 818, 620] 4 | } 5 | 6 | const shoppingCart = { 7 | products: [ 8 | {price: 150}, 9 | {price: 502}, 10 | {price: 97}, 11 | {price: 818}, 12 | {price: 620} 13 | ], 14 | }; 15 | 16 | const prices = getPriceArray(shoppingCart); 17 | 18 | console.log(`Prices: ${prices}`); 19 | -------------------------------------------------------------------------------- /5-collections/47.js: -------------------------------------------------------------------------------- 1 | function getPriceArray(shoppingCart) { 2 | let array = []; 3 | shoppingCart.products.forEach(function (product) { 4 | array.push(product.price); 5 | }); 6 | return array; 7 | } 8 | 9 | const shoppingCart = { 10 | products: [ 11 | {price: 150}, 12 | {price: 502}, 13 | {price: 97}, 14 | {price: 818}, 15 | {price: 620} 16 | ], 17 | }; 18 | 19 | const prices = getPriceArray(shoppingCart); 20 | 21 | console.log(`Prices: ${JSON.stringify(prices)}`); 22 | -------------------------------------------------------------------------------- /5-collections/48.js: -------------------------------------------------------------------------------- 1 | function getPriceArray(shoppingCart) { 2 | return shoppingCart.products.map(product => product.price); 3 | } 4 | 5 | const shoppingCart = { 6 | products: [ 7 | {price: 150}, 8 | {price: 502}, 9 | {price: 97}, 10 | {price: 818}, 11 | {price: 620} 12 | ], 13 | }; 14 | 15 | const prices = getPriceArray(shoppingCart); 16 | 17 | console.log(`Prices: ${JSON.stringify(prices)}`); 18 | -------------------------------------------------------------------------------- /5-collections/49.js: -------------------------------------------------------------------------------- 1 | function myArrayMap(operation, array) { 2 | let newArray = []; 3 | for (let i = 0; i < array.length; i++) { 4 | const item = array[i]; 5 | const newItem = operation(item); 6 | newArray.push(newItem); 7 | } 8 | return newArray; 9 | } 10 | 11 | function getPriceArray(shoppingCart) { 12 | return myArrayMap(product => product.price, shoppingCart.products); 13 | } 14 | 15 | const shoppingCart = { 16 | products: [ 17 | {price: 150}, 18 | {price: 502}, 19 | {price: 97}, 20 | {price: 818}, 21 | {price: 620} 22 | ], 23 | }; 24 | 25 | const prices = getPriceArray(shoppingCart); 26 | 27 | console.log(`Prices: ${JSON.stringify(prices)}`); 28 | -------------------------------------------------------------------------------- /5-collections/50.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | function myArrayMap(operation) { 4 | return function myArrayMapWithOperation(array) { 5 | let newArray = []; 6 | for (let i = 0; i < array.length; i++) { 7 | const item = array[i]; 8 | const newItem = operation(item); 9 | newArray.push(newItem); 10 | } 11 | return newArray; 12 | }; 13 | } 14 | 15 | const getProducts = shoppingCart => shoppingCart.products; 16 | const mapToPrices = myArrayMap(product => product.price); 17 | const getPriceArray = _.pipe(getProducts, mapToPrices); 18 | 19 | const shoppingCart = { 20 | products: [ 21 | {price: 150}, 22 | {price: 502}, 23 | {price: 97}, 24 | {price: 818}, 25 | {price: 620} 26 | ], 27 | }; 28 | 29 | const prices = getPriceArray(shoppingCart); 30 | 31 | console.log(`Prices: ${JSON.stringify(prices)}`); 32 | -------------------------------------------------------------------------------- /5-collections/51.js: -------------------------------------------------------------------------------- 1 | const shoppingCart = { 2 | products: [ 3 | {price: 150}, 4 | {price: 502}, 5 | {price: 97}, 6 | {price: 818}, 7 | {price: 620} 8 | ], 9 | }; 10 | 11 | const prices = shoppingCart.products.map(product => product.price); 12 | 13 | console.log(`Prices: ${JSON.stringify(prices)}`); 14 | -------------------------------------------------------------------------------- /5-collections/52.js: -------------------------------------------------------------------------------- 1 | function getExpensiveProducts(shoppingCart) { 2 | let array = []; 3 | shoppingCart.products.forEach(function (product) { 4 | if (product.price > 300) { 5 | array.push(product); 6 | } 7 | }); 8 | return array; 9 | } 10 | 11 | const shoppingCart = { 12 | products: [ 13 | {price: 150}, 14 | {price: 502}, 15 | {price: 97}, 16 | {price: 818}, 17 | {price: 620} 18 | ], 19 | }; 20 | 21 | const products = getExpensiveProducts(shoppingCart); 22 | 23 | console.log(`Expensive products: ${JSON.stringify(products)}`); 24 | -------------------------------------------------------------------------------- /5-collections/53.js: -------------------------------------------------------------------------------- 1 | function getExpensiveProducts(shoppingCart) { 2 | return shoppingCart.products.filter(product => product.price > 300); 3 | } 4 | 5 | const shoppingCart = { 6 | products: [ 7 | {price: 150}, 8 | {price: 502}, 9 | {price: 97}, 10 | {price: 818}, 11 | {price: 620} 12 | ], 13 | }; 14 | 15 | const products = getExpensiveProducts(shoppingCart); 16 | 17 | console.log(`Expensive products: ${JSON.stringify(products)}`); 18 | -------------------------------------------------------------------------------- /5-collections/54.js: -------------------------------------------------------------------------------- 1 | function myArrayFilter(condition) { 2 | return function myArrayFilterWithCondition(array) { 3 | let newArray = []; 4 | for (let i = 0; i < array.length; i++) { 5 | const item = array[i]; 6 | if (condition(item)) { 7 | newArray.push(item); 8 | } 9 | }; 10 | return newArray; 11 | }; 12 | } 13 | 14 | function getExpensiveProducts(shoppingCart) { 15 | return myArrayFilter(product => product.price > 300)(shoppingCart.products); 16 | } 17 | 18 | const shoppingCart = { 19 | products: [ 20 | {price: 150}, 21 | {price: 502}, 22 | {price: 97}, 23 | {price: 818}, 24 | {price: 620} 25 | ], 26 | }; 27 | 28 | const products = getExpensiveProducts(shoppingCart); 29 | 30 | console.log(`Expensive products: ${JSON.stringify(products)}`); 31 | -------------------------------------------------------------------------------- /5-collections/55.js: -------------------------------------------------------------------------------- 1 | const shoppingCart = { 2 | products: [ 3 | {price: 150}, 4 | {price: 502}, 5 | {price: 97}, 6 | {price: 818}, 7 | {price: 620} 8 | ], 9 | }; 10 | 11 | const products = shoppingCart.products.filter(product => product.price > 300); 12 | 13 | console.log(`Expensive products: ${JSON.stringify(products)}`); 14 | -------------------------------------------------------------------------------- /5-collections/56-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 56.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | const shoppingCart = { 30 | products: [ 31 | {price: 150}, 32 | {price: 502}, 33 | {price: 97}, 34 | {price: 818}, 35 | {price: 620} 36 | ], 37 | }; 38 | 39 | const expensivePrices = shoppingCart.products 40 | .filter(product => product.price > 300) 41 | .map(product => product.price); 42 | 43 | console.log(`Expensive prices: ${JSON.stringify(expensivePrices)}`); 44 | -------------------------------------------------------------------------------- /5-collections/56.js: -------------------------------------------------------------------------------- 1 | const shoppingCart = { 2 | products: [ 3 | {price: 150}, 4 | {price: 502}, 5 | {price: 97}, 6 | {price: 818}, 7 | {price: 620} 8 | ], 9 | }; 10 | 11 | // Make this using the new functions we've learned so far 12 | const expensivePrices = // ... 13 | 14 | console.log(`Expensive prices: ${JSON.stringify(expensivePrices)}`); 15 | // Should output: 16 | // > [502,818,620] 17 | -------------------------------------------------------------------------------- /5-collections/57.js: -------------------------------------------------------------------------------- 1 | function getTotal(shoppingCart) { 2 | let total = 0; 3 | shoppingCart.products.forEach(function (product) { 4 | total = total + product.price; 5 | }); 6 | return total; 7 | } 8 | 9 | const shoppingCart = { 10 | products: [ 11 | {price: 150}, 12 | {price: 502}, 13 | {price: 97}, 14 | {price: 818}, 15 | {price: 620} 16 | ], 17 | }; 18 | 19 | const total = getTotal(shoppingCart); 20 | 21 | console.log(`Total in shopping cart: ${JSON.stringify(total)}`); 22 | -------------------------------------------------------------------------------- /5-collections/58.js: -------------------------------------------------------------------------------- 1 | function getTotal(shoppingCart) { 2 | return shoppingCart.products 3 | .reduce((prevTotal, product) => prevTotal + product.price, 0); 4 | } 5 | 6 | const shoppingCart = { 7 | products: [ 8 | {price: 150}, 9 | {price: 502}, 10 | {price: 97}, 11 | {price: 818}, 12 | {price: 620} 13 | ], 14 | }; 15 | 16 | const total = getTotal(shoppingCart); 17 | 18 | console.log(`Total in shopping cart: ${JSON.stringify(total)}`); 19 | -------------------------------------------------------------------------------- /5-collections/59.js: -------------------------------------------------------------------------------- 1 | function myArrayReduce(accumulate, seed) { 2 | return function myArrayReduceWithAccumulate(array) { 3 | let result = seed; 4 | for (let i = 0; i < array.length; i++) { 5 | const item = array[i]; 6 | result = accumulate(result, item); 7 | }; 8 | return result; 9 | }; 10 | } 11 | 12 | function getTotal(shoppingCart) { 13 | return myArrayReduce((acc, p) => acc + p.price, 0)(shoppingCart.products); 14 | } 15 | 16 | const shoppingCart = { 17 | products: [ 18 | {price: 150}, 19 | {price: 502}, 20 | {price: 97}, 21 | {price: 818}, 22 | {price: 620} 23 | ], 24 | }; 25 | 26 | const total = getTotal(shoppingCart); 27 | 28 | console.log(`Total in shopping cart: ${JSON.stringify(total)}`); 29 | -------------------------------------------------------------------------------- /5-collections/60-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 60.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | const array = ['1', '2', '4', null, 'a', '6', '8', '9']; 30 | 31 | const sum = array 32 | .map(x => parseInt(x)) 33 | .filter(x => !isNaN(x)) 34 | .reduce((acc, x) => acc + x, 0); 35 | 36 | console.log(sum); 37 | -------------------------------------------------------------------------------- /5-collections/60.js: -------------------------------------------------------------------------------- 1 | const array = ['1', '2', '4', null, 'a', '6', '8', '9']; 2 | 3 | // Calculate this constant as the sum of all *numbers* (only) in the array. 4 | // The console output should report: 5 | // > 30 6 | const sum = // ... 7 | 8 | console.log(sum); 9 | -------------------------------------------------------------------------------- /5-collections/61-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 61.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | import _ from 'lodash/fp'; 30 | import Immutable from 'immutable'; 31 | 32 | function increaseFirstProductPrice(shoppingCart) { 33 | return shoppingCart.update('products', products => 34 | products.update(0, product => 35 | product.update('price', price => 36 | price + 100 37 | ) 38 | ) 39 | ); 40 | } 41 | 42 | function totalInShoppingCart(shoppingCart) { 43 | return shoppingCart.get('products') 44 | .map(product => product.get('price')) 45 | .reduce((acc, price) => acc + price); 46 | } 47 | 48 | function updateInvoiceTotal(invoice) { 49 | return function (newTotal) { 50 | return invoice.set('total', newTotal); 51 | }; 52 | } 53 | 54 | function discount(invoice) { 55 | return invoice.update('total', total => total * 0.9); 56 | } 57 | 58 | const invoice = Immutable.fromJS({ 59 | total: 2055, 60 | }); 61 | console.log(`Invoice before: ${invoice.get('total')}`); 62 | 63 | const calculateTotal = _.pipe( 64 | increaseFirstProductPrice, 65 | totalInShoppingCart, 66 | updateInvoiceTotal(invoice), 67 | discount 68 | ); 69 | 70 | const shoppingCart = Immutable.fromJS({ 71 | products: [ 72 | {price: 150}, 73 | {price: 502}, 74 | {price: 97}, 75 | {price: 818}, 76 | {price: 620} 77 | ], 78 | }); 79 | 80 | const newInvoice = calculateTotal(shoppingCart); 81 | 82 | console.log(`Invoice after: ${invoice.get('total')}`); 83 | console.log(`New invoice: ${newInvoice.get('total')}`); 84 | -------------------------------------------------------------------------------- /5-collections/61.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | function increaseFirstProductPrice(shoppingCart) { 5 | return shoppingCart.update('products', products => 6 | products.update(0, product => 7 | product.update('price', price => 8 | price + 100 9 | ) 10 | ) 11 | ); 12 | } 13 | 14 | function totalInShoppingCart(shoppingCart) { 15 | // Replace this for loop with functional collection manipulations 16 | let result = 0; 17 | const length = shoppingCart.get('products').size; 18 | for (let i = 0; i < length; i++) { 19 | result += shoppingCart.get('products').get(i).get('price'); 20 | } 21 | return result; 22 | } 23 | 24 | function updateInvoiceTotal(invoice) { 25 | return function (newTotal) { 26 | return invoice.set('total', newTotal); 27 | }; 28 | } 29 | 30 | function discount(invoice) { 31 | return invoice.update('total', total => total * 0.9); 32 | } 33 | 34 | const invoice = Immutable.fromJS({ 35 | total: 2055, 36 | }); 37 | console.log(`Invoice before: ${invoice.get('total')}`); 38 | 39 | const calculateTotal = _.pipe( 40 | increaseFirstProductPrice, 41 | totalInShoppingCart, 42 | updateInvoiceTotal(invoice), 43 | discount 44 | ); 45 | 46 | const shoppingCart = Immutable.fromJS({ 47 | products: [ 48 | {price: 150}, 49 | {price: 502}, 50 | {price: 97}, 51 | {price: 818}, 52 | {price: 620} 53 | ], 54 | }); 55 | 56 | const newInvoice = calculateTotal(shoppingCart); 57 | 58 | console.log(`Invoice after: ${invoice.get('total')}`); 59 | console.log(`New invoice: ${newInvoice.get('total')}`); 60 | // The console output should report: 61 | // > New invoice: 2058.3 62 | -------------------------------------------------------------------------------- /5-collections/62.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | 3 | const arr1 = [1, 2, 4, 8, 16]; 4 | const arr2 = [2, 4, 6, 8, 10]; 5 | 6 | const diff = _.intersection(arr1, arr2); 7 | console.log(diff); // [2, 4, 8] 8 | -------------------------------------------------------------------------------- /5-collections/63-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 63.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | import _ from 'lodash/fp'; 30 | import Immutable from 'immutable'; 31 | 32 | const people = [ 33 | {name: 'Lisa Pisa', gender: 'female', height: 1.65, weight: 57}, 34 | {name: 'Marko Starko', gender: 'male', height: 1.72, weight: 85}, 35 | {name: 'James Bond', gender: 'male', height: 1.57, weight: 64}, 36 | {name: 'Jacky Chan', gender: 'male', height: 1.84, weight: 116}, 37 | {name: 'Marco Polo', gender: 'male', height: 1.58, weight: 66}, 38 | {name: 'Lara Croft', gender: 'female', height: 1.88, weight: 69}, 39 | {name: 'Mary Antoinette', gender: 'female', height: 1.61, weight: 89}, 40 | {name: 'Obi-wan Kenobi', gender: 'male', height: 1.98, weight: 111}, 41 | {name: 'Leonardo Da Vinci', gender: 'male', height: 1.98, weight: 63}, 42 | {name: 'Marylyn Monroe', gender: 'female', height: 1.65, weight: 62}, 43 | {name: 'Lizzie Frizzle', gender: 'female', height: 1.52, weight: 95}, 44 | {name: 'Leia Organa', gender: 'female', height: 1.75, weight: 69} 45 | ]; 46 | 47 | const peopleWithCategories = Immutable.fromJS(people) 48 | .map((person) => { 49 | const h = person.get('height'); 50 | const w = person.get('weight'); 51 | const bmi = w / (h * h); 52 | if (bmi < 18.5) { 53 | return person.set('category', 'underweight'); 54 | } else if (bmi < 25) { 55 | return person.set('category', 'normal'); 56 | } else if (bmi < 30) { 57 | return person.set('category', 'overweight'); 58 | } else { 59 | return person.set('category', 'obese'); 60 | } 61 | }); 62 | 63 | 64 | const overweightMen = peopleWithCategories 65 | .filter(p => p.get('gender') === 'male' && p.get('category') === 'overweight') 66 | .sortBy(p => p.get('name')) 67 | .map(p => p.get('name')) 68 | .toArray(); 69 | 70 | console.log(overweightMen); 71 | -------------------------------------------------------------------------------- /5-collections/63.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash/fp'; 2 | import Immutable from 'immutable'; 3 | 4 | const people = [ 5 | {name: 'Lisa Pisa', gender: 'female', height: 1.65, weight: 57}, 6 | {name: 'Marko Starko', gender: 'male', height: 1.72, weight: 85}, 7 | {name: 'James Bond', gender: 'male', height: 1.57, weight: 64}, 8 | {name: 'Jacky Chan', gender: 'male', height: 1.84, weight: 116}, 9 | {name: 'Marco Polo', gender: 'male', height: 1.58, weight: 66}, 10 | {name: 'Lara Croft', gender: 'female', height: 1.88, weight: 69}, 11 | {name: 'Mary Antoinette', gender: 'female', height: 1.61, weight: 89}, 12 | {name: 'Obi-wan Kenobi', gender: 'male', height: 1.98, weight: 111}, 13 | {name: 'Leonardo Da Vinci', gender: 'male', height: 1.98, weight: 63}, 14 | {name: 'Marylyn Monroe', gender: 'female', height: 1.65, weight: 62}, 15 | {name: 'Lizzie Frizzle', gender: 'female', height: 1.52, weight: 95}, 16 | {name: 'Leia Organa', gender: 'female', height: 1.75, weight: 69} 17 | ]; 18 | 19 | // Given the data above, calculate the BMI (Body-Mass Index) of each 20 | // person, and then add a field `category` to each one of them such that: 21 | // BMI < 18.5: 'underweight' 22 | // 18.5 <= BMI < 25.0: 'normal' 23 | // 25.0 <= BMI < 30.0: 'overweight' 24 | // 30.0 <= BMI : 'obese' 25 | // 26 | // Then, create the array overweightMen that contains only the *names* of 27 | // overweight men, sorted by name. 28 | // All of this should be done in an immutable and functional manner. 29 | // To make sure, after the console.log, the people array should stay unmodified. 30 | 31 | const overweightMen = // array of sorted names of men in the overweight category 32 | 33 | console.log(overweightMen); 34 | // Console should show: 35 | // > ["James Bond", "Marco Polo", "Marko Starko", "Obi-wan Kenobi"] 36 | -------------------------------------------------------------------------------- /5-collections/64.js: -------------------------------------------------------------------------------- 1 | function myArrayReduce(accumulate, seed) { 2 | return function myArrayReduceWithAccumulate(array) { 3 | let result = seed; 4 | for (let i = 0; i < array.length; i++) { 5 | const item = array[i]; 6 | result = accumulate(result, item); 7 | }; 8 | return result; 9 | }; 10 | } 11 | 12 | // notice the similarities 13 | 14 | function myArrayMap(operation) { 15 | return function myArrayMapWithOperation(array) { 16 | let newArray = []; 17 | for (let i = 0; i < array.length; i++) { 18 | const item = array[i]; 19 | const newItem = operation(item); 20 | newArray.push(newItem); 21 | } 22 | return newArray; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /5-collections/65-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 65.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | function myArrayMap(operation) { 30 | return function myArrayMapWithOperation(array) { 31 | return array.reduce((acc, x) => acc.concat(operation(x)), []) 32 | }; 33 | } 34 | 35 | const shoppingCart = { 36 | products: [ 37 | {price: 150}, 38 | {price: 502}, 39 | {price: 97}, 40 | {price: 818}, 41 | {price: 620} 42 | ], 43 | }; 44 | 45 | const prices = myArrayMap(product => product.price)(shoppingCart.products); 46 | 47 | console.log(`Prices: ${JSON.stringify(prices)}`); 48 | -------------------------------------------------------------------------------- /5-collections/65.js: -------------------------------------------------------------------------------- 1 | function myArrayMap(operation) { 2 | return function myArrayMapWithOperation(array) { 3 | // Using the conventional array.reduce, no for loops nor mutable variables, 4 | // implement this function so that it behaves just like an array.map 5 | }; 6 | } 7 | 8 | // Test it by running the code below 9 | 10 | const shoppingCart = { 11 | products: [ 12 | {price: 150}, 13 | {price: 502}, 14 | {price: 97}, 15 | {price: 818}, 16 | {price: 620} 17 | ], 18 | }; 19 | 20 | const prices = myArrayMap(product => product.price)(shoppingCart.products); 21 | 22 | console.log(`Prices: ${JSON.stringify(prices)}`); 23 | -------------------------------------------------------------------------------- /5-collections/66-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 66.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | function myArrayFilter(condition) { 30 | return function myArrayFilterWithCondition(array) { 31 | return array.reduce((acc, x) => { 32 | if (condition(x)) { 33 | return acc.concat(x); 34 | } else { 35 | return acc; 36 | } 37 | }, []) 38 | }; 39 | } 40 | 41 | const shoppingCart = { 42 | products: [ 43 | {price: 150}, 44 | {price: 502}, 45 | {price: 97}, 46 | {price: 818}, 47 | {price: 620} 48 | ], 49 | }; 50 | 51 | const expensiveProducts = myArrayFilter(p => p.price > 300)(shoppingCart.products); 52 | 53 | console.log(`Expensive products: ${JSON.stringify(expensiveProducts)}`); 54 | -------------------------------------------------------------------------------- /5-collections/66.js: -------------------------------------------------------------------------------- 1 | function myArrayFilter(condition) { 2 | return function myArrayFilterWithCondition(array) { 3 | // Using the conventional array.reduce, no for loops nor mutable variables, 4 | // implement this function so that it behaves just like an array.filter 5 | }; 6 | } 7 | 8 | // Test it by running the code below 9 | 10 | const shoppingCart = { 11 | products: [ 12 | {price: 150}, 13 | {price: 502}, 14 | {price: 97}, 15 | {price: 818}, 16 | {price: 620} 17 | ], 18 | }; 19 | 20 | const expensiveProducts = myArrayFilter(p => p.price > 300)(shoppingCart.products); 21 | 22 | console.log(`Expensive products: ${JSON.stringify(expensiveProducts)}`); 23 | -------------------------------------------------------------------------------- /6-async/67.js: -------------------------------------------------------------------------------- 1 | function clickCallback(clickEvent) { 2 | console.log(clickEvent.clientX); 3 | } 4 | 5 | document.addEventListener('click', clickCallback); 6 | -------------------------------------------------------------------------------- /6-async/68.js: -------------------------------------------------------------------------------- 1 | function successCallback(response) { 2 | console.log(response); 3 | } 4 | function failureCallback(error) { 5 | console.error(error); 6 | } 7 | 8 | fetch('http://my.test.server.com/api') 9 | .then(r => successCallback(r)) 10 | .catch(e => failureCallback(e)); 11 | -------------------------------------------------------------------------------- /6-async/69.js: -------------------------------------------------------------------------------- 1 | function nextDataCallback(chunk) { 2 | console.log(chunk); 3 | } 4 | function failureCallback(err) { 5 | console.error(err); 6 | } 7 | function doneCallback() { 8 | console.log('We are done streaming'); 9 | } 10 | 11 | const readable = getReadableStreamSomehow(); 12 | readable.on('data', nextDataCallback); 13 | readable.on('error', failureCallback); 14 | readable.on('end', doneCallback); 15 | -------------------------------------------------------------------------------- /6-async/70.js: -------------------------------------------------------------------------------- 1 | function nextDataCallback(data) { 2 | // ... 3 | } 4 | function failureCallback(err) { 5 | // ... 6 | } 7 | function doneCallback() { 8 | // ... 9 | } 10 | 11 | doSomeAsyncStuff(nextDataCallback, failureCallback, doneCallback); 12 | -------------------------------------------------------------------------------- /6-async/71.js: -------------------------------------------------------------------------------- 1 | function next(data) { 2 | // ... 3 | } 4 | function error(err) { 5 | // ... 6 | } 7 | function complete() { 8 | // ... 9 | } 10 | 11 | subscribeToObservable(next, error, complete); 12 | -------------------------------------------------------------------------------- /6-async/72.js: -------------------------------------------------------------------------------- 1 | function next(clickEvent) { 2 | console.log(clickEvent.clientX); 3 | } 4 | function error(err) { 5 | // ... 6 | } 7 | function complete() { 8 | // ... 9 | } 10 | 11 | function subscribeToClickObservable(next, error, complete) { 12 | try { 13 | document.addEventListener('click', next); 14 | } catch (err) { 15 | error(err); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /6-async/73.js: -------------------------------------------------------------------------------- 1 | function createObservable(subscribe) { 2 | return { 3 | subscribe: subscribe 4 | } 5 | } 6 | 7 | const clicksObservable = createObservable(function (next, error, complete) { 8 | try { 9 | document.addEventListener('click', next); 10 | } catch (err) { 11 | error(err); 12 | } 13 | }); 14 | 15 | clicksObservable.subscribe( 16 | function next(clickEvent) { 17 | console.log(clickEvent.clientX); 18 | }, 19 | function error(err) { 20 | // ... 21 | }, 22 | function complete() { 23 | // ... 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /6-async/74.js: -------------------------------------------------------------------------------- 1 | function createObservable(subscribe) { 2 | return { 3 | subscribe: subscribe 4 | } 5 | } 6 | 7 | const clicksObservable = createObservable(function (next, error, complete) { 8 | try { 9 | document.addEventListener('click', next); 10 | } catch (err) { 11 | error(err); 12 | } 13 | }); 14 | 15 | clicksObservable.subscribe( 16 | function next(clickEvent) { 17 | if (clickEvent.clientX > 200) { 18 | console.log(clickEvent.clientX); 19 | } 20 | }, 21 | function error(err) { 22 | // ... 23 | }, 24 | function complete() { 25 | // ... 26 | } 27 | ); 28 | -------------------------------------------------------------------------------- /6-async/75-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 75.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | const arrayClicks = [ 30 | {clientX: 528}, 31 | {clientX: 130}, 32 | {clientX: 255}, 33 | {clientX: 308}, 34 | {clientX: 40}, 35 | ]; 36 | 37 | const largeX = arrayClicks 38 | .map(click => click.clientX) 39 | .filter(x => x > 200); 40 | 41 | largeX.forEach(x => console.log(x)); 42 | -------------------------------------------------------------------------------- /6-async/75.js: -------------------------------------------------------------------------------- 1 | const arrayClicks = [ 2 | {clientX: 528}, 3 | {clientX: 130}, 4 | {clientX: 255}, 5 | {clientX: 308}, 6 | {clientX: 40}, 7 | ]; 8 | 9 | const largeX = // Using functional collection APIs you *already* know 10 | // for arrays, create largeX to be an array of x position 11 | // numbers from the arrayClicks that are larger than 200. 12 | 13 | largeX.forEach(x => console.log(x)); 14 | -------------------------------------------------------------------------------- /6-async/76.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const clicksObservable = Observable.fromEvent(document, 'click'); 4 | const xPositions = clicksObservable.map(click => click.clientX); 5 | const largeX = xPositions.filter(x => x > 200); 6 | 7 | largeX.subscribe(x => console.log(x)); 8 | -------------------------------------------------------------------------------- /6-async/77.js: -------------------------------------------------------------------------------- 1 | function createObservable(subscribe) { 2 | return { 3 | subscribe: subscribe, 4 | } 5 | } 6 | 7 | function myObservableMap(operation) { 8 | return function myObservableMapGivenOperation(sourceObservable) { 9 | return createObservable(function (next, error, complete) { 10 | sourceObservable.subscribe(function (data) { 11 | next(operation(data)) 12 | }, error, complete); 13 | }); 14 | } 15 | } 16 | 17 | const clicksObservable = createObservable(function (next, error, complete) { 18 | try { 19 | document.addEventListener('click', next); 20 | } catch (err) { 21 | error(err); 22 | } 23 | }); 24 | 25 | const xPositions = myObservableMap(event => event.clientX)(clicksObservable); 26 | 27 | xPositions.subscribe( 28 | function next(x) { 29 | if (x > 200) { 30 | console.log(x); 31 | } 32 | }, 33 | function error(err) { 34 | // ... 35 | }, 36 | function complete() { 37 | // ... 38 | } 39 | ); 40 | -------------------------------------------------------------------------------- /6-async/78-solution.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SOLUTION to exercise 78.js in this file. 3 | * 4 | * Please try to actually solve it before looking 5 | * at the solution. 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * Ready? 27 | * 28 | */ 29 | function createObservable(subscribe) { 30 | return { 31 | subscribe: subscribe, 32 | } 33 | } 34 | 35 | function myObservableMap(operation) { 36 | return function myObservableMapGivenOperation(sourceObservable) { 37 | return createObservable(function (next, error, complete) { 38 | sourceObservable.subscribe(function (data) { 39 | next(operation(data)) 40 | }, error, complete); 41 | }); 42 | } 43 | } 44 | 45 | function myObservableFilter(condition) { 46 | return function myObservableFilterGivenCondition(sourceObservable) { 47 | return createObservable(function (next, error, complete) { 48 | sourceObservable.subscribe(function (data) { 49 | if (condition(data)) { 50 | next(data); 51 | } 52 | }, error, complete); 53 | }); 54 | } 55 | } 56 | 57 | const clicksObservable = createObservable(function (next, error, complete) { 58 | try { 59 | document.addEventListener('click', next); 60 | } catch (err) { 61 | error(err); 62 | } 63 | }); 64 | 65 | const xPositions = myObservableMap(event => event.clientX)(clicksObservable); 66 | const largeX = myObservableFilter(x => x > 200)(xPositions); 67 | 68 | largeX.subscribe( 69 | function next(x) { 70 | console.log(x); 71 | }, 72 | function error(err) { 73 | // ... 74 | }, 75 | function complete() { 76 | // ... 77 | } 78 | ); 79 | -------------------------------------------------------------------------------- /6-async/78.js: -------------------------------------------------------------------------------- 1 | function createObservable(subscribe) { 2 | return { 3 | subscribe: subscribe, 4 | } 5 | } 6 | 7 | function myObservableMap(operation) { 8 | return function myObservableMapGivenOperation(sourceObservable) { 9 | return createObservable(function (next, error, complete) { 10 | sourceObservable.subscribe(function (data) { 11 | next(operation(data)) 12 | }, error, complete); 13 | }); 14 | } 15 | } 16 | 17 | function myObservableFilter(condition) { 18 | return function myObservableFilterGivenCondition(sourceObservable) { 19 | // Write this function 20 | } 21 | } 22 | 23 | const clicksObservable = createObservable(function (next, error, complete) { 24 | try { 25 | document.addEventListener('click', next); 26 | } catch (err) { 27 | error(err); 28 | } 29 | }); 30 | 31 | const xPositions = myObservableMap(event => event.clientX)(clicksObservable); 32 | const largeX = myObservableFilter(x => x > 200)(xPositions); 33 | 34 | largeX.subscribe( 35 | function next(x) { 36 | console.log(x); 37 | }, 38 | function error(err) { 39 | // ... 40 | }, 41 | function complete() { 42 | // ... 43 | } 44 | ); 45 | -------------------------------------------------------------------------------- /6-async/79.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const observable = Observable.create(function subscribe(observer) { 4 | observer.next(10); 5 | observer.next(20); 6 | observer.next(30); 7 | setTimeout(function () { 8 | observer.next(60) 9 | }, 1000); 10 | }); 11 | 12 | const observer = { 13 | next: (x) => console.log('Observer got: ' + x), 14 | error: (e) => console.error(e), 15 | complete: () => console.log('done'), 16 | }; 17 | 18 | console.log('Before subscribe'); 19 | observable.subscribe(observer); 20 | console.log('After subscribe'); 21 | -------------------------------------------------------------------------------- /6-async/80.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const observable = Observable.from([1, 4, 9, 16, 25]); 4 | 5 | const observer = { 6 | next: (x) => console.log('Observer got: ' + x), 7 | error: (e) => console.error(e), 8 | complete: () => console.log('done'), 9 | }; 10 | 11 | console.log('Before subscribe'); 12 | observable.subscribe(observer); 13 | console.log('After subscribe'); 14 | -------------------------------------------------------------------------------- /6-async/81.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const observable = Observable.interval(1000); 4 | 5 | const observer = { 6 | next: (x) => console.log('Observer got: ' + x), 7 | error: (e) => console.error(e), 8 | complete: () => console.log('done'), 9 | }; 10 | 11 | console.log('Before subscribe'); 12 | observable.subscribe(observer); 13 | console.log('After subscribe'); 14 | -------------------------------------------------------------------------------- /6-async/82-solution.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const observable = Observable.interval(1000) 4 | .filter(x => x % 2 === 0) 5 | .take(10) 6 | .map(x => x * 100); 7 | 8 | const observer = { 9 | next: (x) => console.log('Observer got: ' + x), 10 | error: (e) => console.error(e), 11 | complete: () => console.log('done'), 12 | }; 13 | 14 | console.log('Before subscribe'); 15 | observable.subscribe(observer); 16 | console.log('After subscribe'); 17 | -------------------------------------------------------------------------------- /6-async/82.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const observable = // Make this Observable to emit 10 even numbers, each 4 | // multiplied by 100, and they should be emitted every 5 | // 2 seconds. Read http://reactivex.io/rxjs/ to see 6 | // documentation on Observable operators. 7 | 8 | const observer = { 9 | next: (x) => console.log('Observer got: ' + x), 10 | error: (e) => console.error(e), 11 | complete: () => console.log('done'), 12 | }; 13 | 14 | console.log('Before subscribe'); 15 | observable.subscribe(observer); 16 | console.log('After subscribe'); 17 | -------------------------------------------------------------------------------- /7-io/83.js: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | 3 | const observable = Observable.interval(1000) 4 | .filter(x => x % 2 === 0) 5 | .take(10) 6 | .map(x => x * 100); 7 | 8 | const observer = { 9 | next: (x) => console.log('Observer got: ' + x), 10 | error: (e) => console.error(e), 11 | complete: () => console.log('done'), 12 | }; 13 | 14 | console.log('Before subscribe'); 15 | observable.subscribe(observer); 16 | console.log('After subscribe'); 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | 20 | function createObservable(subscribe) { 21 | return { 22 | subscribe: subscribe 23 | } 24 | } 25 | 26 | const clicksObservable = createObservable(function (next, error, complete) { 27 | try { 28 | document.addEventListener('click', next); 29 | } catch (err) { 30 | error(err); 31 | } 32 | }); 33 | 34 | clicksObservable.subscribe( 35 | function next(clickEvent) { 36 | if (clickEvent.clientX > 200) { 37 | console.log(clickEvent.clientX); 38 | } 39 | }, 40 | function error(err) { 41 | // ... 42 | }, 43 | function complete() { 44 | // ... 45 | } 46 | ); 47 | -------------------------------------------------------------------------------- /7-io/84.js: -------------------------------------------------------------------------------- 1 | import Cycle from '@cycle/xstream-run'; 2 | import xs from 'xstream'; 3 | import {div, label, input, hr, h1, makeDOMDriver} from '@cycle/dom'; 4 | 5 | function main(sources) { 6 | return { 7 | DOM: sources.DOM.select('.myinput').events('input') 8 | .map(ev => ev.target.value) 9 | .startWith('') 10 | .map(name => 11 | div([ 12 | label('Name:'), 13 | input('.myinput', {attrs: {type: 'text'}}), 14 | hr(), 15 | h1(`Hello ${name}`) 16 | ]) 17 | ) 18 | }; 19 | } 20 | 21 | Cycle.run(main, { 22 | DOM: makeDOMDriver('#app') 23 | }); 24 | 25 | // http://www.webpackbin.com/Vyt3FP8MZ 26 | -------------------------------------------------------------------------------- /7-io/85.js: -------------------------------------------------------------------------------- 1 | function greet(name) { 2 | return { 3 | type: 'console.log', 4 | payload: `Welcome ${name}!`, 5 | }; 6 | } 7 | 8 | var instruction = greet('Richard'); 9 | if (instruction.type === 'console.log') { 10 | console.log(instruction.payload); 11 | } 12 | -------------------------------------------------------------------------------- /7-io/86.js: -------------------------------------------------------------------------------- 1 | // Pure: 2 | 3 | function greet(name) { 4 | return { 5 | type: 'console.log', 6 | payload: `Welcome ${name}!`, 7 | }; 8 | } 9 | 10 | function sendRequest(url) { 11 | return { 12 | type: 'fetch', 13 | payload: url, 14 | }; 15 | } 16 | 17 | var instructions = [ 18 | greet('Richard'), 19 | sendRequest('http://google.com') 20 | ]; 21 | 22 | // Impure: 23 | 24 | instructions.forEach(instruction => { 25 | switch (instruction.type) { 26 | case 'console.log': 27 | console.log(instruction.payload); 28 | break; 29 | case 'fetch': 30 | fetch(instruction.payload); 31 | break; 32 | default: 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /7-io/87.js: -------------------------------------------------------------------------------- 1 | // http://www.webpackbin.com/NynshO8GZ 2 | import React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { fromJS } from 'immutable'; 5 | import { createStore } from 'redux'; 6 | import { Provider, connect } from 'react-redux'; 7 | import { createAction, createReducer } from 'redux-act'; 8 | import { Effects, loop, install } from 'redux-loop'; 9 | 10 | export default function delay (milliseconds) { 11 | return new Promise((resolve, reject) => { 12 | const shouldFail = Math.random() <= 0.2; 13 | setTimeout(() => { 14 | if(shouldFail) reject(); 15 | else resolve(); 16 | }, milliseconds); 17 | }); 18 | } 19 | 20 | /** 21 | * Set up an initial state with two counters that we will update after some 22 | * variable amount of time after we start the incrementing process. Immutable 23 | * objects work rather well with redux-loop, and there is not additional setup 24 | * to be able to use an Immutable.Map as your top level state. Internally, 25 | * redux-loop makes sure the redux store is satisfied with the shape of your 26 | * state. 27 | */ 28 | const initialState = fromJS({ 29 | short: { 30 | loading: false, 31 | count: 0, 32 | failed: false 33 | }, 34 | long: { 35 | loading: false, 36 | count: 0, 37 | failed: false 38 | } 39 | }); 40 | 41 | 42 | /** 43 | * We're using redux-act (https://github.com/pauldijou/redux-act) to set up 44 | * some actions here, but this is just because we like it and it lets you add 45 | * real documentation strings instead of using constants. No special 46 | * action-creator factories or reducer factories are necessary to use 47 | * redux-loop. Switch statements are totally fine! 48 | */ 49 | const Actions = { 50 | incrementBothStart: createAction('Start both increments'), 51 | shortIncrementStart: createAction('Start short incrememt'), 52 | shortIncrementSucceed: createAction('Finish short increment'), 53 | shortIncrementFail: createAction('Fail to complete short increment'), 54 | longIncrementStart: createAction('Start long increment'), 55 | longIncrementSucceed: createAction('Finish long increment'), 56 | longIncrementFail: createAction('Fail to complete long increment') 57 | }; 58 | 59 | 60 | /** 61 | * In order to do actual real-world things in your application you'll probably 62 | * have an API that makes network requests or manages animations or something 63 | * of that nature. Here we'll just use timeouts to mock up some asynchronous 64 | * behavior. redux-loop expects that your Promises will always resolve, even if 65 | * the original API call fails. You'll always want to fill in both the `then` 66 | * and the `catch` cases with an action creator. In this example, the `delay` 67 | * function has a roughly 50% chance of rejecting the promise, so we'll handle 68 | * both cases. If you forget to handle an error case and a Promise passed to 69 | * redux-loop does get rejected, you'll see an error logged to the console and 70 | * redux-loop's dispatching will halt. 71 | */ 72 | const Api = { 73 | shortIncrement: (amount) => ( 74 | delay(10) 75 | .then(() => Actions.shortIncrementSucceed(amount)) 76 | .catch(() => Actions.shortIncrementFail()) 77 | ), 78 | 79 | longIncrement: (amount) => ( 80 | delay(3000) 81 | .then(() => Actions.longIncrementSucceed(amount)) 82 | .catch(() => Actions.longIncrementFail()) 83 | ) 84 | }; 85 | 86 | 87 | /** 88 | * In your reducer you can choose to return a loop or not. The typical pattern 89 | * you'll end up using is to have some sort of `__Start` action that feeds into 90 | * one or more pairs of `__Succeed` and `__Fail` actions. You must always handle 91 | * the failure case, even if the handler is a no-op! 92 | */ 93 | const reducer = createReducer({ 94 | 95 | /** 96 | * The following three reducers start through the process of updating the 97 | * counter on the short timer. The process starts here and can either fail 98 | * or succeed randomly, and we've covered both cases. 99 | */ 100 | [Actions.shortIncrementStart]: (state, amount) => loop( 101 | state 102 | .setIn(['short', 'loading'], true) 103 | .setIn(['short', 'failed'], false), 104 | Effects.promise(Api.shortIncrement, amount) 105 | ), 106 | 107 | [Actions.shortIncrementSucceed]: (state, amount) => ( 108 | state 109 | .setIn(['short', 'loading'], false) 110 | .updateIn(['short', 'count'], (current) => current + amount) 111 | ), 112 | 113 | [Actions.shortIncrementFail]: (state) => ( 114 | state 115 | .setIn(['short', 'loading'], false) 116 | .setIn(['short', 'failed'], true) 117 | ), 118 | 119 | /** 120 | * The following three reducers perform the same such behavior for the counter 121 | * on the long timer. 122 | */ 123 | [Actions.longIncrementStart]: (state, amount) => loop( 124 | state 125 | .setIn(['long', 'loading'], true) 126 | .setIn(['long', 'failed'], false), 127 | Effects.promise(Api.longIncrement, amount) 128 | ), 129 | 130 | [Actions.longIncrementSucceed]: (state, amount) => ( 131 | state 132 | .setIn(['long', 'loading'], false) 133 | .updateIn(['long', 'count'], (current) => current + amount) 134 | ), 135 | 136 | [Actions.longIncrementFail]: (state) => ( 137 | state 138 | .setIn(['long', 'loading'], false) 139 | .setIn(['long', 'failed'], true) 140 | ), 141 | 142 | /** 143 | * This final action groups the two increment start actions with a batch. 144 | * `Effects.batch()` will wait for all actions in the batch to become available 145 | * before proceeding, so only use it if you want to wait for the longest- 146 | * running effect to complete before dispatching everything. In our case we 147 | * want both increment paths to procede independently so we use 148 | * `Effects.constant()` to forward into the starting actions for both paths. 149 | */ 150 | [Actions.incrementBothStart]: (state, amount) => loop( 151 | state, 152 | Effects.batch([ 153 | Effects.constant(Actions.shortIncrementStart(amount)), 154 | Effects.constant(Actions.longIncrementStart(amount)) 155 | ]) 156 | ) 157 | }, initialState); 158 | 159 | 160 | /** 161 | * Here we set up a simple, reusable "dumb" component in the redux nomenclature 162 | * which we can reuse to render each counter since they have a repeatable 163 | * structure. 164 | */ 165 | const Counter = ({ loading, count, failed, onClick, name }) => { 166 | const failureMessage = failed ? 167 |