I found using .filter() .map() and .reduce() Array methods could help to reduce code complexity and simplify many typical array data processing tasks. Looking at http://kangax.github.io/compat-table/es5/#test-Array_methods, you can confirm these methods are compatible with almost any modern desktop/mobile browser.
A good approach to understand how to use .filter() .map() and .reduce() is to compare each one to SQL data operations in SELECT statements.
Consider the following SQL query:
SELECT category, SUM(amount) as total FROM transactions WHERE status = 'active' GROUP BY category
A common SQL command could have three type of data operations: map or select fields to display ( category, total ), filter data using conditions (status = ‘active’), and reduce the results generating summary data using an aggregate function (SUM) specifying some aggregate groups (category). In Javascript, these operations could be performed using .filter() .map() and .reduce()
Given the following data set in a Javascript array of objects, I will explain and show you some usage examples for each array method:
var transactions = [ { id: 4534, date: "2019–01–08", product: 112, price: 21, quantity: 2, taxes: 1.12, declined: false }, { id: 4535, date: "2019–01–08", product: 232, price: 32, quantity: 3, taxes: 2.19, declined: false }, { id: 4536, date: "2019–01–08", product: 554, price: 7, quantity: 100, taxes: 10.55, declined: true }, { id: 4537, date: "2019–01–08", product: 433, price: 21, quantity: 2, taxes: 1.12, declined: false }, { id: 4538, date: "2019–01–08", product: 112, price: 21, quantity: 4, taxes: 2.24, declined: false } ];
filter()
We use filter() as the first operation to perform, because it reduces the amount of rows to process information.
result = myArray.filter( filterFunc )
filterFunc takes one element of the array each time, and must return true to include (or false to exclude) that item from the result.
For example:
var activeTransactions = transactions.filter(function(item){ return !item.declined })
Now activeTransactions
will only contain transactions where declined
is true
. In this example, 4 out of 5. This is the equivalent of the following SQL query:
SELECT * FROM transactions WHERE NOT declined
map()
We can use map() to select which fields will be part of the result.
result = myArray.map( mapFunc )
mapFunc takes one element of myArray
each time, and must return a value or object mapping element attributes.
var totals = transactions.map(function(item){ return { productId: item.product, totalAmount : item.price * item.quantity + item.taxes } })
Now totals
will contain an array of objects, each one with only 2 fields: productId (alias of .product
) and totalAmount (calculated from .price
.quantity
and .taxes
). This is equivalent to the following SQL query:
SELECT product as productId, price*quantity+taxes as totalAmount FROM transactions
reduce()
Finally, we can get a summary of values (or just a single final value) from the dataset, reducing rows to a single element.
result = myArray.reduce( reduceFunc, initialAccum )
reduceFunc takes an accumulator and each element of myArray, accumulate values, and return the final summary.initialAccum
is the initial value of your result, starting with values to be accumulated (for example, starting with zero).
var total = transactions.reduce(function(accum, item){ return { total: accum.total + item.quantity*item.price+item.taxes, count: accum.count + 1 } }, {total: 0, count: 0} )
Now total
will contain a grand total amount total
and how many transactions (count
) were processed. This is equivalent to the following SQL query:
SELECT SUM(price*quantity+taxes) as 'total', count(1) as 'count' FROM transactions
Sometimes, you only need to return a single final value, in this case your accumulator must be a single value (0
in the next example):
var total = transactions.reduce(function(accum, item){ return accum + item.quantity*item.price+item.taxes }, 0 )
Mixing all together
Finally, because .filter() .map() and .reduce() return arrays, you can put everything together in a chain of calls to make a final result like this:
// Total amount of transactions not declined var total = transactions .filter(function(item){ return !item.declined }) .map(function(item){ return { totalAmount : item.price * item.quantity + item.taxes } }) .reduce(function(accum, item){ return accum + item.totalAmount }, 0 )
This is equivalent to the result of the following SQL query:
SELECT SUM(price*quantity+taxes) FROM transactions WHERE NOT declined
Do it yourself
You can try for yourself running and modifying the following Javascript demo with more examples:
https://jsfiddle.net/FranIg/3pn4rs5c/