I’ll try to explain it and show the difference. But don’t worry if it doesn’t click right away. It’s not an easy concept to grasp quickly.
And don’t feel the need to learn this right now either. It’s not critical. Feel free to let me know if there’s something I didn’t make clear.
I blurred it cuz it contains spoilers.
Two of the main concepts of functional programming are
1. everything is data. We use functions to modify and display that data. Like a sculptor molds clay, so you must mold the data.
2.\ the original data must not be changed (immutability)
So let’s take a look at the requirements. I always list them out like this so that I can knock each out as it’s own function.
If any step requires more steps, then I break it down further. But none of these do.
1. the global array bookList is not changed inside either function
2. The add function should add the given bookName to the end of an array
3. The remove function should remove the given bookName from an array.
4. Both functions should return an array
5. new parameters should be added before the bookName one
Since 1 is an array, and 4 requires we return an array, we know that we can use array methods. Step 5 is just guidance on the parameters. So really we only have 3 requirements
1. Use array methods
2. The add function should add the given bookName to the end of an array
3. The remove function should remove the given bookName from an array.
Ok. So if we were going to code this imperatively, for step 2 (the add function) we’d be saying to ourselves
Using an array of books and the name of a newBook
1. make a copy of the books array
2. push the newBook to the books array
3. return to me the copy of the books array
Whereas declaratively, we’d be saying
Using an array of books and the name of a newBook
1. give me a book array with the new book merged to the end
And that’s it.
But you may wonder if the declarative version is just the question rephrased. It is.
The difference is the key word I used — merged. Remember how I said in functional programming you can only modify data? That means we can’t add to an array. We must merge new data in with the old data, into a new container (immutability).
So once you realize that all you really have to do is merge two arrays together, you can then use the .concat()
method on the array object.
From mdn:
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
And we get a bonus. Since it returns a new array, you don’t have to worry about creating a copy.
You would then use it like this:
function add (bookLists,bookName) {
// merge an array with bookName into bookLists
return bookList.concat([bookName])
}
See how I put the bookName inside an array literal? That’s because .concat()
merges two arrays. So I just created one to take advantage of the array method.
You can do this any time you feel that using an array method will make things easier. Creating arrays are inexpensive. Browsers can create over 50 million of them a second.
This is declarative because we’re not telling the computer how to create a copy or how to attach the new item. We just say “hey, concat (merge) this new book to the end of the booklist”
Here is how we would do the remove function declaratively
Using an array of books and the name of an existing book
1. give me an array with that item filtered out.
Why filter? Because filtering is just another name for removing things. Now we can use filter()
method because it also returns a new array.
function remove (bookLists,bookName) {
// give me all the books that don't match the bookName
return bookList.filter(b => b !== bookName)
}
Why this song and dance? Because once you grasp the concept, it will lead to clearer and more maintainable code.
What you should do is look over these methods and create a mental map of what they do, using everyday words that make sense to you. My most used and preferred ones are:
array.map()
— transform every item in the array according to some logic
[1,2].map(it => it * 2) // [2,4]
[1,2].map(it => ({ it: it * 2})) // [{ 1: 2 }, { 2: 4 }]
array.filter()
— remove any items I do not want
[1,2].filter(it => it !== 2) // [1]
array.concat()
— attach these two arrays together
[1,2].concat([3,4]) // [1,2,3,4]
array.reduce()
— take all of one thing, and give me back one of something
[a, b].reduce((prev, curr, idx) => {
prev[curr] = idx
return prev } ,{}) // {a: 1, b: 2}
These are the main array methods I find myself using.
.map()
and .reduce()
are some of the most powerful methods. And if you can get a good grasp on them, you’ll be able to solve many problems with them.