I wrote an answer a while ago, and my opinion has changed. I recommend checking out my blog post that extends this topic and explains it much better. It also gives a JSperf comparison at the end of alternatives.
Tl; dr is this: to accomplish what you ask for (filtering and displaying within a single function call), you must use Array.reduce() . However, a more readable and usually faster 2 approach is to simply use a filter and the cards are connected to each other:
[1,2,3].filter(num => num > 2).map(num => num * 2)
The following is a description of how Array.reduce() works, and how it can be used to perform filtering and matching in one iteration. If this is too concise, I highly recommend looking at the blog post linked above, which is a much more friendly introduction with clear examples and progress.
You give to reduce the argument, which is a (usually anonymous) function.
This anonymous function takes two parameters - one (like the anonymous functions passed to map / filter / forEach) is the iterator you need to work with. However, for the anonymous function, another argument is passed, namely that these functions are not accepted, and this is the value that will be passed between function calls, often called a reminder .
Note that while Array.filter () takes only one argument (function), Array.reduce () also takes an important (albeit optional) second argument: the initial value for memo, which will be passed to this anonymous function as its first argument, and then can be mutated and passed between function calls. (If it is not specified, then by default “memo” in the first call of the anonymous function will be the first iterator, and the argument of the “iterator” will actually be the second value in the array)
In our case, we will pass an empty array to run, and then choose whether to insert our iterator into our array or not based on our function - this is a filtering process.
Finally, we will return our “array in process” with every call to the anonymous function, and Reduce will take this return value and pass it as an argument (called memo) to the next function call.
This allows you to filter and display the map in one iteration, halving the number of necessary iterations. :)
For a more complete explanation, refer to MDN or the link above. :)
A basic example of calling Reduce:
let array = [1,2,3]; const initialMemo = []; array = array.reduce((memo, iteratee) => { // if condition is our filter if (iteratee > 1) { // what happens inside the filter is the map memo.push(iteratee * 2); } // this return value will be passed in as the 'memo' argument // to the next call of this function, and this function will have // every element passed into it at some point. return memo; }, initialMemo) console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
shorter version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Please note that the first iterable was no more than one and therefore was filtered. Also pay attention to initialMemo, named simply to clarify its existence and draw attention to it. Once again, it is passed as a “reminder” to the first call of the anonymous function, and then the return value of the anonymous function is passed as the argument of the “note” to the next function.
Another example of the classic use case for memo is to return the smallest or largest number in an array. Example:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val) // ^this would return the largest number in the list.
An example of how to write your own cast function (this often helps to understand functions like these, I find):
test_arr = []; // we accept an anonymous function, and an optional 'initial memo' value. test_arr.my_reducer = function(reduceFunc, initialMemo) { // if we did not pass in a second argument, then our first memo value // will be whatever is in index zero. (Otherwise, it will // be that second argument.) const initialMemoIsIndexZero = arguments.length < 2; // here we use that logic to set the memo value accordingly. let memo = initialMemoIsIndexZero ? this[0] : initialMemo; // here we use that same boolean to decide whether the first // value we pass in as iteratee is either the first or second // element const initialIteratee = initialMemoIsIndexZero ? 1 : 0; for (var i = initialIteratee; i < this.length; i++) { // memo is either the argument passed in above, or the // first item in the list. initialIteratee is either the // first item in the list, or the second item in the list. memo = reduceFunc(memo, this[i]); } // after we've compressed the array into a single value, // we return it. return memo; }
The actual implementation allows you to access things like the index, for example, but I hope this helps you easily understand the point.