Last active
June 20, 2018 15:22
-
-
Save GreggSetzer/a31bb28a955e6d410380b12faa52ca1d to your computer and use it in GitHub Desktop.
Simplifying JavaScript code using Functional Programming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
* Given an array of orders, find the top 3 customers with the | |
* highest lifetime value (those that spent the most over time). | |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
// Start with the initial implementation. | |
// | |
// One approach to solving this task would be to create | |
// a map of the customerId and a sum of their order totals. | |
// Convert this map to an array so we can sort the orders | |
// from highest to lowest. Display the top three customers. | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
const orders = [ | |
{orderId: 1, customerId: 1, total: 10.50}, | |
{orderId: 2, customerId: 1, total: 11.50}, | |
{orderId: 3, customerId: 2, total: 12.50}, | |
{orderId: 4, customerId: 3, total: 130.50}, | |
{orderId: 5, customerId: 7, total: 24.50}, | |
{orderId: 6, customerId: 2, total: 64.50}, | |
{orderId: 7, customerId: 4, total: 40.50}, | |
{orderId: 8, customerId: 4, total: 29.50}, | |
{orderId: 9, customerId: 6, total: 61.50}, | |
{orderId: 10, customerId: 7, total: 110.00} | |
]; | |
//Build a temporary table to track total orders for a customer. | |
let orderTotalsMap = {}; | |
for (let i = 0; i < orders.length; i++) { | |
let order = orders[i]; | |
if (orderTotalsMap[order.customerId] === undefined) { | |
orderTotalsMap[order.customerId] = order.total; | |
} else { | |
orderTotalsMap[order.customerId] += order.total; | |
} | |
} | |
//Convert the map to an array and sort by the aggregated order total. | |
const keysArr = Object.keys(orderTotalsMap); | |
let orderTotalsArr = []; | |
for (let i = 0; i < keysArr.length; i++) { | |
const totals = { | |
customerId: keysArr[i], | |
total: orderTotalsMap[keysArr[i]] | |
} | |
orderTotalsArr.push(totals); | |
} | |
orderTotalsArr.sort(function (a, b) { | |
return b.total - a.total; | |
}); | |
const topThreeCustomers = orderTotalsArr.slice(0, 3); | |
console.log('The top three customers:', topThreeCustomers); | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
// It's difficult to understand what's going on in the | |
// example above. With some refactoring, this can be much | |
// more legible. | |
// | |
// To fulfill our requirements, we must: | |
// 1. Get the lifetime value for each customer. | |
// 2. Sort by the total amount and return the top 3 customers. | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
const orders = [ | |
{orderId: 1, customerId: 1, total: 10.50}, | |
{orderId: 2, customerId: 1, total: 11.50}, | |
{orderId: 3, customerId: 2, total: 12.50}, | |
{orderId: 4, customerId: 3, total: 130.50}, | |
{orderId: 5, customerId: 7, total: 24.50}, | |
{orderId: 6, customerId: 2, total: 64.50}, | |
{orderId: 7, customerId: 4, total: 40.50}, | |
{orderId: 8, customerId: 4, total: 29.50}, | |
{orderId: 9, customerId: 6, total: 61.50}, | |
{orderId: 10, customerId: 7, total: 110.00} | |
]; | |
//Get order totals for each customer. | |
const getCustomerLifetimeValues = (acc, order) => { | |
const {customerId, total} = order; | |
if (acc[customerId] === undefined) { | |
acc[customerId] = {customerId, total} | |
} else { | |
acc[customerId].total += total; | |
} | |
return acc; | |
} | |
//Comparator for sorting by total. | |
const comparator = (a, b) => b.total - a.total; | |
//Now, for the code that matters! Let's worry less about "how this works" and more about the "why we need it". | |
let lifetimeValuesObj = orders.reduce(getCustomerLifetimeValues, {}); | |
let topThreeCustomers = Object.values(lifetimeValuesObj).sort(comparator).slice(0, 3); | |
console.log('The top three customers:', topThreeCustomers); | |
// For this solution, we are able to make reasable assumptions about the code because it's much easier to understand. | |
// By looking at lines 102 - 103, we know that the code will calculate the totals for each customer and provide top | |
// three customers by order totals. We didn't have to study the implementation to determine what is going on. Sweet! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
* You are given a task to reduce the elements in an array by 1, | |
* then, sum the values that are divisible by three. How would | |
* you accomplish this? | |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
// Start with the initial implementation. | |
// | |
// Using a for loop, it might look something like this. | |
// This approach uses more cognitive processing power, | |
// because, you are required to understand the how this | |
// code works and why we are using it. | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; | |
let sum = 0; | |
for (let i = 0; i < numbers.length; i++) { | |
numbers[i] = numbers[i] - 1; | |
if (numbers[i] % 3 === 0) { | |
sum += numbers[i]; | |
} | |
} | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
// Refactor #1. | |
// | |
// Using array helpers and function chaining, it might look | |
// something like this. This is an improvement, you focus | |
// more on the why and less on the how. But it is still | |
// difficult to read. | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; | |
//Use function chaining with array helpers to get the result. | |
const result = numbers.map(n => n-1).reduce((acc, n) => acc += (n % 3 === 0) ? n : 0, 0); | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
// Refactor #2. | |
// | |
// Refactor the array helpers to increase readability. | |
// In the process, get easier to test code by using pure | |
// functions. Most importantly, we can match the code up | |
// to our requirements. | |
// | |
// Array helpers: | |
// Map - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map | |
// Reduce - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce | |
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; | |
const subtractOne = (n) => { | |
return n - 1; | |
} | |
const sumDivisibleByThree = (acc, n) => { | |
return acc += (n % 3 === 0) ? n : 0; | |
} | |
//Much easier to read! Compare this to the original solution. | |
const result = numbers.map(subtractOne).reduce(sumDivisibleByThree, 0); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment