~ or ~
Imagine a simple todo list spec...
Everything has to be kept in sync
Every possible permutation has to be coded for
What about searching within a filtered list?
What about the next feature request?
function ProgressBar({ done, total }) {
let innerClassName = 'progress-inner';
// Don't divide by zero
const width = (total === 0 ?
'0' :
(100 * done / total) + '%'
);
if (total !== 0 && done === total) {
innerClassName += ' all-done';
}
return (
<div className="progress-outer">
<div
className={innerClassName}
style={{ width: width }}
/>
</div>
);
}
<ProgressBar done={done} total={total} />
filterTodos(todos = this.state.todos, filter = this.state.filter) {
if (filter === 'complete') {
return todos.filter(t => t.complete);
}
if (filter === 'incomplete') {
return todos.filter(t => !t.complete);
}
return todos;
}
searchTodos(todos = this.state.todos, search = this.state.search) {
return todos.filter(t => (t.name.indexOf(search) !== -1));
}
const allTodos = this.state.todos;
const todos = this.filterTodos(this.searchTodos(allTodos));
We saw declarative UI, now let's look at a more declarative style for functions themselves.
function getLowestRegularShowtimePrice(showtimes) {
var lowest = Math.Infinity;
for (var i=0; i < showtimes.length; ++i) {
var showtime = showtimes[i];
if (hasRegularTickets(showtime)) {
for (var j=0; j < showtime.regularTicketSections; ++j) {
var section = showtime.regularTicketSections[j];
if (section.price < lowest) {
lowest = section.price;
}
}
}
}
return lowest;
}
const getLowestRegularShowtimePrice = _.flow(
fp.filter(hasRegularTickets),
fp.map(_.flow(
fp.get('regularTicketSections'),
fp.map('price'),
fp.min
)),
fp.min
);
Maybe not at first glance. Let's back up...
Allows the programmer to only partially fill in the arguments of a function, defining another function that takes the rest.
threeWords(a, b, c) {
return a + ' ' + b + ' ' + c;
}
twoWords = threeWords('hey'); // function that takes b and c
oneWord = twoWords('there'); // function that takes c
oneWord('world'); // 'hey there world'
map(fn, arr) {
acc = [];
for (var i=0; i < arr.length; ++i) {
acc.push(fn(acc[i]));
}
return acc;
}
allPrices = map(item => item.price);
allPrices([{ price: 3 }, { price: 3.5 }]) // => [3, 3.5]
Combines functions together, so the result of one becomes the input of the other.
compose(f, g)(x) === f(g(x))
Front-end team usually uses _.flow
, which is the same thing
reversed.
_.flow(f, g)(x) === g(f(x))
Or, the way my mind tends to parse it...
tmp = f(x)
return g(tmp)
min(arr) {
lowest = arr[0];
for (var i=1; i < arr.length; ++i) {
if (arr[i] < lowest) {
lowest = arr[i];
}
}
return lowest;
}
lowestPrice = _.flow(allPrices, min);
lowestPrice([{ price: 3 }, { price: 3.5 }]) // => 3
const getLowestRegularShowtimePrice = _.flow(
fp.filter(hasRegularTickets),
fp.map(_.flow(
fp.get('regularTicketSections'),
fp.map('price'),
fp.min
)),
fp.min
);