An Elegant way For…a Method Array
Learning methods for code and coding languages, require a first principle approach to methods of implementation. My experience with programming languages over the years has taught me that there are many similar software constructs that most well engineered, software languages utilize. Two software constructs that are almost universal in all common software languages are Arrays and For…loops. Arrays and For…loops pair well together, so much so that they are often as the defacto way you typically iterate over an array.
When I first learned JavaScript and needed to iterate over an array, like everybody else, I used a For...loop. I also used other variations of the For…loop, such as the For…in and then the For…of, when I started learning ES6 syntax principles, but even that was all based on the same conventional pattern of a For…loop, but with syntactic sugar on it.
Then I started to learn Vue.js and React.js and both had a method built into arrays called .map() and it allowed a more elegant solution for array iteration. For…loops are not the only ways to iterate over an array. The .map() method isn’t the only method available to iterate over an array.
A friend of mine told me,
“In Javascript, if you are using a For…loop to iterate over an array, then you are probably not optimizing in a elegant manner.”
The methods built into Javascript arrays are extremely useful. Learn them well, and adopt them in practice. That alone will distinguish you as a developer elite, just remember the last time you have actually used a for…loop in JavaScript, to iterate over an array. Of the various Array methods, the three most useful have got to be .map(), .filter() and .reduce() and it will improve your game when you know how to use them.
Mutating Methods vs Non-Mutating Methods
Array methods can, for the most part, be grouped into one of two groups:
Mutating
Mutating methods change the actual array that it is called on. These methods include splice push/pop and shift/unshift. map filter and reduce.
Non-Mutating
Non-Mutating methods means that the underlining array is left unchanged when the function is finished.
Footnote:
I would also mention that I have 3 other Array methods that I use most frequently. These are .forEach() .join() and .sort(). This will be explored another time, but you can learn about them at MDN.
The Map Method
The .map() method creates a new array with the results of calling a provided function on every element, for example:
const items = [1,2,3,4]; // Create const array construct
const doubled = items.map(x => x * 2);// .map() to doubled
console.log(doubled) // console results
// [2,4,6,8]
It’s important to understand that this new array will ALWAYS be the same size as the original array, even if you don’t return anything. The .map() method is used when you need to do some type of transform on every element in the array. The .map() actually passes in a 2nd and 3rd argument, but they are used much less often, you can find out more about this at MDN
The Filter Method
The .filter() method creates a new array with all items that pass the test implemented by the function that is passed into it. Unlike the map method, filter will return an array that is the same size or smaller (it will always return an array though, even a empty array).
For example:
const items = [1,2,3,4];
const justEven = items.filter(x => x%2 === 0);
console.log(justEven) // [2,4]
There are 2 important things to pay attention to when using the .filter() method. First, is that it doesn’t change any of the underlying items, it simply adds them to the new array if it passes the conditional test. Second, is that you need to know of JavaScript’s truthy/falsy gotchas. The function is expecting a boolean to be returned, but if you return something that is not a boolean, it will type cast or coerce it into a boolean. This is where understanding the nuances of JavaScript's truthy and falsy are important.
Just like .map() there is a 2nd and 3rd argument that is passed to the function, learn more about this at MDN’s documentation on the .filter() method.
The Reduce Method
The .reduce() method is the most versatile method out of all of them. It applies a function against an accumulator and each element in the array to reduce it to a single value. The function passed takes in 2 arguments, the accumulator (acc) and current item (cur), and what is returned is the accumulator for the next function call. Once it has been run on all of the items in the array, then the final accumulator value is returned. For example:
const items = [1,2,3,4];
const sumOfItems =
items.reduce((acc,cur) => acc + cur);
console.log(sumOfItems) // 10
// end
The .reduce() method, in addition to the function parameter, takes an optional second parameter value. If you pass in this value, it will be used as the initial value of the accumulator, otherwise, the value at the 0 index will be used as the initial value and function won't be called on that item. Even though it is optional, I highly advise you to not leave it out. Using the above example you can see how much something can change by changing the initial value:
const items = [1,2,3,4]; // start
items.reduce((acc,cur) => acc + cur, 0) // 10
items.reduce((acc,cur) => acc + cur, '') // '1234'
// end
/*
Once again, there is an optional 3rd and 4th argument
that you can learn more about at MDN
*/
Method Chaining
The great thing about these methods is that you can chain them together. So if you need to filter and then reduce you can, simply by chaining them together like this:
const bookObject =
bookArray
.filter(book => !book.read)
.map(({title, author}) => ({title, author}))
.reduce((acc, cur)=> {
acc[cur.title] = cur;
return acc;
},{});
In the above example, we are filtering down to the books we haven’t read, then taking out only the title and author for each book. Finally, in the reduce, I am turning the array into an object, using the title as a key to each book.
Arrays are such an important data structure in any language, and JavaScript has some powerful ways to make it easier to iterate over them.