I've mentioned previously that D3 is very functionally designed, meaning that it uses some of the idioms JavaScript has adopted from functional programming. Although we can still approach D3 development in a very classical, object-oriented fashion, our lives will be much easier if we start thinking about our code and data with a functional mindset.
The good news is that JavaScript almost counts as a functional language; there are enough features to get the benefits of a functional style, and it also provides enough freedom to do things imperatively or in an object-oriented way. The bad news is that, unlike real functional languages, the environment gives no guarantee about our code.
In this section, we'll go through the basics of functional-style coding and look at wrangling the data so that it's easier to work with. If you want to try proper functional programming, I suggest that you refer to Haskell in Learn You a Haskell for Great Good, free to read and available at http://learnyouahaskell.com/.
The idea behind functional programming is simple: compute by relying only on function arguments. It's simple, but its consequences are far reaching.
The biggest consequence is that we don't have to rely on state, which in turn gives us a referential transparency. This means that functions executed with the same parameters will always give the same results, regardless of when or how they're called.
In practice, this means that we design the code and dataflow, that is, get data as an input, execute a sequence of functions that pass changed data down the chain, and eventually get a result.
It also emphasizes immutability, whereby functions are written to prevent side-effects and changes to the underlying data as it passes through your code. In Chapter 2, A Primer on DOM, SVG, and CSS, we used Array.from() to make a copy of an argument so that our function wouldn't mutate the data passed to it. We also generally assign variables using the const keyword, so we know to make a copy, and will get errors if we try to reassign a variable. We'll continue to do this to some extent though we mainly make use of ES6's concept of immutability, insomuch that it only protects against reassignment using the const keyword, we still have the ability to extend objects and interact with arrays using methods, such as Array.prototype.pop() and Array.prototype.unshift(), as per usual.
In the following examples, I give the full names of the native prototype methods so as to differentiate them from d3-selection's filter and map methods. Array.prototype.map, thus, refers to the map method on the Array primitive's prototype object.
You've already seen this functional approach in previous examples, particularly in Chapter 2, A Primer on DOM, SVG, and CSS. Our dataset started and ended as an array of values. We performed some actions for each item, and we relied only on the current item when deciding what to do. We also had the current index so that we could cheat a little with an imperative approach by looking ahead and behind in the stream.
Although I endeavor to take a somewhat functional approach throughout the rest of the book, there's no way I could adequately explain the nuances of what is a really interesting and important practice within modern JavaScript development. For a good series on the topic, I suggest Eric Elliott's excellent series that can be found at https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c.