Nunca escribas un loop de nuevo
Mateo Calle M.
@Mathius17
Un poquito de mi antes de seguir
Disclaimer!
Basado en el libro Refactoring to Collections de Adam Wathan
Programación
imperativa vs declarativa
Imperativa
function getUserEmails(users) {
let emails = [];
for (let i = 0; i < users.length; i++) {
const user = users[i];
if (user.email) {
emails.push(user.email);
}
}
return emails;
}
Cómo se hacen las cosas
Declarativa
SELECT email
FROM users
WHERE email IS NOT NULL
Qué queremos hacer
Funciones de orden superior
Es una función que toma una función como parámetro, retorna una función, o ambas.
function twice(f, param) {
return f(f(param));
}
function add5(value) {
return value + 5;
}
twice(add5, 10); // 20
Abstrayendo patrones a funciones de orden superior
Tenemos una lista de clientes y queremos saber los correos de cada uno
Tenemos un inventario y queremos saber el valor de inventario para cada producto
let customerEmails = [];
for (let i = 0; i < customers.length; i++) {
const customer = customers[i];
customerEmails.push(
customer.email
);
}
return customerEmails;
let stockTotals = [];
for (let i = 0; i < inventory.length; i++) {
const item = inventory[i];
stockTotals.push({
product: item.productName,
totalValue: item.quantity * item.price,
});
}
return stockTotals;
// let customerEmails = [];
// for (let i = 0; i < customers.length; i++) {
// const customer = customers[i];
// customerEmails.push(
customer.email
// );
// }
// return customerEmails;
// let stockTotals = [];
// for (let i = 0; i < inventory.length; i++) {
// const item = inventory[i];
// stockTotals.push(
{
product: item.productName,
totalValue: item.quantity * item.price,
}
// );
// }
// return stockTotals;
let customerEmails = [];
for (let i = 0; i < customers.length; i++) {
const customer = customers[i];
customerEmails.push(
// customer.email
);
}
return customerEmails;
let stockTotals = [];
for (let i = 0; i < inventory.length; i++) {
const item = inventory[i];
stockTotals.push(
// {
// product: item.productName,
// totalValue: item.quantity * item.price,
// }
);
}
return stockTotals;
function map(items, func) {
let results = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
results.push(func(item));
}
return results;
}
let customerEmails = [];
for (let i = 0; i < customers.length; i++) {
const customer = customers[i];
customerEmails.push(
customer.email
);
}
return customerEmails;
let stockTotals = [];
for (let i = 0; i < inventory.length; i++) {
const item = inventory[i];
stockTotals.push({
product: item.productName,
totalValue: item.quantity * item.price,
});
}
return stockTotals;
let customerEmails = map(
customers,
customer => customer.email
);
let stockTotals = map(
inventory,
item => {
product: item.productName,
totalValue: item.quantity * item.price,
}
);
Funciones básicas
Each / ForEach
function each(items, callback) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
callback(item);
}
}
Imprimir los nombres y apellidos de una lista de personas
for (let i = 0; i < persons.length; i++) {
const person = persons[i];
console.log(`${person.name} ${person.lastname}`);
}
each(persons, person => {
console.log(`${person.name} ${person.lastname}`);
});
Map
function map(items, callback) {
let results = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
results.push(callback(item));
}
return results;
}
Obtener los correos de una lista de clientes
let emails = [];
for (let i = 0; i < customers.length; i++) {
const customer = customers[i];
emails.push(customer.email);
}
return emails;
const emails = map(customers, customer => customer.email);
Filter
function filter(items, callback) {
let results = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (callback(item)) {
results.push(item);
}
}
return results;
}
Obtener sólo los productos que valgan menos de $100
let filteredProducts = [];
for (let i = 0; i < products.length; i++) {
const product = products[i];
if (product.price < 100) {
filteredProducts.push(product);
}
}
return filteredProducts;
const filteredProducts = filter(
products,
product => product.price < 100
);
Reduce
function reduce(items, callback, initial) {
let accumulator = initial;
for (let i = 0; i < items.length; i++) {
const item = items[i];
accumulator = callback(accumulator, item);
}
return accumulator;
}
El precio total de un carrito de compras
let total = 0;
for (let i = 0; i < cart.items.length; i++) {
const item = cart.items[i];
total += item.price;
}
return total;
const totalPrice = filter(
cart.items,
(total, item) => total + item.price
);
Ya está implementado
-
Array.prototype (Nativo ES5+)
-
Lodash
-
Underscore
-
Ramda
-
Etc
Concatenación y código más legible
const contacts = [
{ name: 'Mateo', email: 'mateo@company.com', ... },
{ name: 'Laura', email: 'laura@company2.com', ... },
{ name: 'Paula', email: 'paula@company.com', ... },
];
// Usando funciones
const count = reduce(
filter(
map(
contacts,
contact => contact.email
),
email => email.includes('@company.com')
),
(carry, email) => carry += `${email}`,
0
);
// Usando los métodos nativos de Array
const count = contacts
.map(contact => contact.email)
.filter(contact => contact.email)
.reduce((carry, email) => carry + 1, 0);
// Usando lodash
const count = _(contacts)
.map(contact => contact.email)
.filter(contact => contact.email)
.reduce((carry, email) => carry + 1, 0);
import { flow, map, flatten, sortBy } from 'lodash/fp';
// Usando `flow` y lodash/fp
flow(
map(x => [x, x*2]),
flatten,
sortBy(x => x)
)([1, 2, 3]);
// En algunos algunos lenguajes funcionales
[1, 2, 3]
|> map(x => [x, x*2])
|> flatten
|> sortBy(x => x);
Ejercicio:
¿Cuál es mi puntaje en GitHub?
https://api.github.com/users/{username}/events
GET request a la siguiente URL:
- PushEvent: 5 puntos
- CreateEvent: 4 puntos
- IssuesEvent: 3 puntos
- CommitCommentEvent: 2 puntos
- Todo lo demás 1 punto
Valor por cada tipo de evento
async function githubScore(username) {
let events = await fetch(`https://api.github.com/users/${username}/events`).then(
response => response.json()
);
// Get all of the event types
let eventTypes = [];
for (let i = 0; i < events.length; i++) {
eventTypes.push(event.type);
}
// Loop over the event types and add up the corresponding scores
let score = 0;
for (let i = 0; i < eventTypes.length; i++) {
switch (eventType[i]) {
case 'PushEvent':
score += 5;
break;
case 'CreateEvent':
score += 4;
break;
case 'IssuesEvent':
score += 3;
break;
case 'CommitCommentEvent':
score += 2;
break;
default:
score += 1;
break;
}
}
return score;
}
async function githubScore(username) {
let events = await fetch(`https://api.github.com/users/${username}/events`).then(
response => response.json()
);
// Get all of the event types
const eventTypes = events.map(event => event.type);
// Loop over the event types and add up the corresponding scores
let score = 0;
for (let i = 0; i < eventTypes.length; i++) {
switch (eventTypes[i]) {
case 'PushEvent':
score += 5;
break;
case 'CreateEvent':
score += 4;
break;
case 'IssuesEvent':
score += 3;
break;
case 'CommitCommentEvent':
score += 2;
break;
default:
score += 1;
break;
}
}
return score;
}
async function githubScore(username) {
let events = await fetch(`https://api.github.com/users/${username}/events`).then(
response => response.json()
);
// Get all of the event types
const eventTypes = events.map(event => event.type);
// Loop over the event types and add up the corresponding scores
const scores = eventTypes.map(eventType => {
switch (eventType) {
case 'PushEvent':
return 5;
case 'CreateEvent':
return 4;
case 'IssuesEvent':
return 3;
case 'CommitCommentEvent':
return 2;
default:
return 1;
}
});
return scores.reduce((carry, value) => carry + value, 0);
}
async function githubScore(username) {
let events = await fetch(`https://api.github.com/users/${username}/events`).then(
response => response.json()
);
return events
.map(event => event.type);
.map(eventType => {
switch (eventType) {
case 'PushEvent':
return 5;
case 'CreateEvent':
return 4;
case 'IssuesEvent':
return 3;
case 'CommitCommentEvent':
return 2;
default:
return 1;
}
})
.reduce((carry, value) => carry + value, 0);
}
async function githubScore(username) {
let events = await fetch(`https://api.github.com/users/${username}/events`).then(
response => response.json()
);
return _(events)
.map('type');
.map(eventType => {
switch (eventType) {
case 'PushEvent':
return 5;
case 'CreateEvent':
return 4;
case 'IssuesEvent':
return 3;
case 'CommitCommentEvent':
return 2;
default:
return 1;
}
})
.sum();
}
async function githubScore(username) {
let events = await fetch(`https://api.github.com/users/${username}/events`).then(
response => response.json()
);
const eventScores = {
'PushEvent': 5,
'CreateEvent': 4,
'IssuesEvent': 3,
'CommitCommentEvent': 2,
};
return _(events)
.map('type')
.map(eventType => eventScores[eventType] || 1)
.sum();
}
¿Preguntas?
Nunca escribas un loop de nuevo
By mathius17
Nunca escribas un loop de nuevo
- 302