Functional Programming with Arrays
If you come from an Object-Oriented Programming background, as I do, you might find functional programming confusing. I know I did when I was first learning it. Starting out with Python left me a bit spoiled! And whatās so bad about for loops anyway?
Okay, well, it turns out functional programming can be very powerful (and its also kind of cool). In this particular post, Iāll talk about three functional methods available on Array.prototype: map, filter and reduce.
To me, this is like the gateway functional programming method. It isnāt too complicated. You call map on an array, and it just does something to everthing in the array, and saves it in a new array. Itās like making a photocopy, only you added a little something - like sticky notes? I donāt know, the metaphor is breaking down.
Iām going to give you an example that involves cats, because I like cats, so there.
Letās start with an array of cat objects:
var cats = [ { name:'Lil Bub', famous: true }, { name: 'Artemis', famous: false }, { name: 'Maru', famous: true }, { name: 'Hubble', famous: false }, { name: 'Grumpy Cat', famous: true }, { name: 'Garfield', famous: true } ];
(Yes, two of those are my cats, of course.)
Letās say we want to add a default favorite food to each cat after the fact. We might be inclined to write do something like this:
var newCatsArray = []; var newCat;
for (var i in cats) { newCat = { name: cats[i].name, famous: cats[i].famous, favoriteFood: cats[i].name === 'Garfield' ? 'lasagna' : 'fish' } newCatsArray.push(newCat) }
Cool. After going through all those iterations, we do end up with a new cat array that does contain a favorite food (fish by default, lasagna if itās Garfield). Thereās nothing really.. wrong with this code. Itās just a bit long, and thereās some stuff in there that you have to remember (for example, using cats[i] to represent the current cat). It just takes you a moment to know whatās happening, and it looks a bit messy. I think we can do better!
Letās try this using map:
var newCatsArray = cats.map(function(cat, i){ return { name: cat.name, famous: cat.famous, favoriteFood: cat.name === 'Garfield' ? 'lasagna' : 'fish' } });
And thatās it! Now newCatsArray is a new array containing cats, their fame status, and their favoriteFood. Itās pretty clear whatās happening - weāre creating a copy of the array with something new. And weāre able have the entire modification happen in nice, simple line of code - weāre just returning the new object.
Next up is filter. I think this method is a fairly easy one to understand once you get the hang of it. Basically, weāll call filter on our array, and as an argument, weāll provide a callback function. Filter will return a new array that only contains the elements of the original array for which the function returned true.
var cats = [ { name:'Lil Bub', famous: true }, { name: 'Artemis', famous: false }, { name: 'Maru', famous: true }, { name: 'Hubble', famous: false }, { name: 'Grumpy Cat', famous: true }, { name: 'Garfield', famous: true } ];
Okay, so I love my cats. But maybe youāre like āI donāt care about your dumb, non-famous cats! Give me a list of just the famous ones!ā
Fine, fine, I understand. Letās get a new array of cats that only includes famous ones:
var famousCats = []; for (var i in cats) { if (cats[i].famous) { famousCats.push(cats[i]); }
}
Hmm, thatās okay, but given what we learned in the last section, Iām sure you can see that there might be a nicer way to do this. Letās see what we can do with filter:
var famousCats = cats.filter(function(cat) { return cat.famous; });
Thatās literally it. How cool is that? Way more readable than the for loop above (even if I do love me a good for loop, I have to admit this essentially one-line piece of code is a lot more elegant!)
Okay, so filter and map may take a moment to get used to, but theyāre pretty straightforward. Things get pretty weird when you start using reduce, but bear with me.
The reduce method is going to take a callback function, too. Instead of going through each item in the array, however, itās going to do something a little different. On each iteration, it will have a previousItem and currentItem argument. Basically, youāre going to be processing 2 items at a time until your array is down to 1 item. And then, you get that one item returned.
A bit confusing, but letās try an example.
Say weāre back to our famous and non-famous cats array:
var cats = [ { name:'Lil Bub', famous: true }, { name: 'Artemis', famous: false }, { name: 'Maru', famous: true }, { name: 'Hubble', famous: false }, { name: 'Grumpy Cat', famous: true }, { name: 'Garfield', famous: true } ];
In the last exercise, you didnāt want to see my cats in the list (mean!). But now, you actually just want to know what are the names of my cats. Thatās it. But you want a string, not an array.
Hmm, this poses a problem. Our other methods return arrays.
Without we might start with a simple iterative approach, after filtering out the famous cats:
var str = "Allie's cat's are named: "; var myCats = []; for (var i in cats) { if (!cats[i].famous) { myCats.push(cats[i].name) }; } str += myCats.join(" & ");
But using filter and reduce together, we can take your array and reduce it down to a single element with no trouble. Check it out:
var str = "Allie's cats are named: "; str += cats.filter(function(cat) { return !cat.famous; }).reduce(function(previousCat, currentCat) { return previousCat.name + " & " + currentCat.name + "."; });
Console logging str, we would see: āAllieās cats are named: Artemis & Hubble.ā
Thatās all for now on this topic. See any errors or have any suggestions? Feel free to comment below, or send me an email.