forked from carlhueffmeier/functional-js-1
/
episode-03.js
147 lines (104 loc) · 4.25 KB
/
episode-03.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Alright, this is not bad!
// If the code wasn't any more complex than this, this could be a good place to stop.
// But... we can do better 😎
// Let me show you the hidden treasures on the other side of function river.
// So, you notice how
// - `Picking properties of objects`
// - `Apply function to variables`
// are pretty common operations that are repeated
// over and over in most of our code.
// Let's see whether we can generalize those operations ✌️
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Episode 3: Return of the Function ~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var axios = require('axios');
var chalk = require('chalk');
// Let's define some general functions to make those repeated operations easier.
// These might look scary and complicated at first, but do not despair!
// Just like the functions we saw in underline, we only need to write them *once*
// After that, we don't have to worry about it!
// One core principal functional programming is relying on your functions
// to work as intended.
// 🆕 identity: Returns the element itself
// Let's start easy!
function identity(x) {
return x;
}
// 🆕 prop: Gets a single property of an object
function prop(key, obj) {
return obj[key];
}
// 🆕 pick: Grabs several properties of an object
// Returns a new object with only the specified properties
function pick(keysToPick, obj) {
return Object.entries(obj).reduce(
(target, [key, val]) => (keysToPick.includes(key) ? { ...target, [key]: val } : target),
{}
);
}
// 🆕 path: Returns the value stored at the specified path
// Example
// var luke = {
// name: { first: 'Luke', last: 'Skywalker' },
// occupation: 'Jedi'
// };
// path(['name', 'last'], luke) -> 'Skywalker'
function path(keys, obj) {
return keys.reduce((target, key) => target[key], obj);
}
// 🆕 sortBy: Returns a sorted copy of the input array
// The `evaluate` function is used to produce a value that can
// be compared via `<`
function sortBy(evaluate, array) {
return [...array].sort((a, b) => (evaluate(a) < evaluate(b) ? -1 : 1));
}
// 🆕 map: Applies a function to every list item and creates a new list from the results
// But you knew that already..
function map(mapFn, array) {
return array.map(mapFn);
}
// 🆕 forEach: Applies a function to every list item
// Working the same as you expect
function forEach(fn, array) {
return array.forEach(fn);
}
// ~~~~~~~~~~~~~~~~~~~~ End of general functions ~~~~~~~~~~~~~~~~~~~
// With a few new tools in our toolbelt, let's try to refactor 💪
// Make the API request
var queryStarWarsMovies = async () => await axios.get('https://swapi.co/api/films/');
// Instead of storing our data,
// let's just define `what` should happen to our data
var getMovies = response => path(['data', 'results'], response);
var pickTitleAndReleaseDate = movie => pick(['title', 'release_date'], movie);
var pickTitleAndReleaseDates = movies => map(pickTitleAndReleaseDate, movies);
// How do we sort by release date?
var getReleaseDateString = movie => prop('release_date', movie);
var convertToDate = dateString => new Date(dateString);
var getReleaseDate = movie => convertToDate(getReleaseDateString(movie));
var sortByReleaseDate = array => sortBy(getReleaseDate, array);
// Create watch list
var formatMovieListItem = (movie, index) => `${index + 1}: ${prop('title', movie)}`;
var typeset = string => chalk.cyanBright.bold(string);
var logToConsole = output => console.log(output);
var printMovie = (movie, index) => {
var listItem = formatMovieListItem(movie, index);
listItem = typeset(listItem);
logToConsole(listItem);
};
var printListOfMovies = movieList => forEach(printMovie, movieList);
// Phew... A job well done 👍
// Wait, you are still here? 🧐
// Oh yeah! We haven't actually DONE anything yet. 😯
// All we did is define functions!
// Let's go ahead and do the actual work 💪
queryStarWarsMovies()
.then(getMovies)
.then(pickTitleAndReleaseDates)
.then(sortByReleaseDate)
.then(printListOfMovies);
// In case you aren't comfortable with promises yet, this is the same as:
// var response = await queryStarWarsMovies();
// var results = getMovies(response);
// var movies = pickTitleAndReleaseDates(results);
// var sortedMovies = sortByReleaseDate(movies);
// printListOfMovies(sortedMovies);