Transcript from the "Reduce: Combining" Lesson
>> Kyle Simpson: So our third operation that we wanna look at, reduce. Reduce is fundamentally a combining operation. Reduce doesn't operate on a single value. Reduce operates on two values at a time. That's a big difference between map and filter. With map and filter, they only operate on a single value.
[00:00:20] Reduce always operates on two values at a time. And when I call it a combining operation, what I mean is we take those two values. And in some way, shape or form, we combine them into one. So, we take values. We combine them into one and then we start over.
[00:00:38] And we say, do I have that value plus another? I need to combine those two and then that value plus another? I need to combine those two and we just keep going through our list until we don't have any more things to combine. So at the end result of a reduce, you typically just have one single discrete value rather than a list.
[00:00:59] Cuz your starting with the first two items and combining them, and then combining that one with the next one, and that one with the next one, and so forth. And at the end, you just have the combined value. You don't have a list anymore. So, reduce is different.
[00:01:12] It doesn't produce a new list, generally speaking.
>> Kyle Simpson: Reduce is actually, probably the most powerful of all the operators and it certainly is powerful enough that it deserves the title of the Swiss Army Knife of functional programming. Because once you learn how to use reduce, you start realizing that a bunch of stuff you do is just a reduce.
[00:01:41] It could be done as a reduce. I could do that as a reduce. I could do that as a reduce. We've already had questions in this course and some people online that have said, well, what about this? Is that a reduce? The answer is yes, because reduce really is the Swiss Army knife that can do a bunch of different things.
[00:01:55] It is not always that it has to return a single discreet value. You can reduce a list to another list. And counterintuitively, you can reduce a list to a list that was even longer than the original. Because reduce is not really about reduction, it's really about combining.
>> Kyle Simpson: So, let's look at building ourselves a reduce utility.
[00:02:25] You'll notice that here, my reducer function which is mult up there. It takes two parameters, cuz it doesn't make any sense to combine a value with itself. We're always combining two values. That's a different shape and that's gonna be really important to keep in your head as we finish through the end of this course, you're gonna wanna keep that in mind.
[00:02:42] That's a different kind of shape and a different kind of operation. We're taking two values. In this case, we're multiplying together. That's how we're combining them. How much you combine two strings?
>> Kyle Simpson: If I had a call to a function called foo and it took two individual strings as arguments and I wanted to return a single value back, what's an example of ways that I could combine those two?
>> Kyle Simpson: I could concatenate the two strings together. That would be a way of combining them, but I can even get more sophisticated. Here's my Swiss Army knife. I could say, the combination of these two is I'm just gonna pick the longer of the two and throw the other one away.
[00:03:33] That's still a valid way of describing the combination. Combining these two individual values could be combining them into an array of the two values. That's a reasonable way to combine them. There's lots and lots, and probably infinite number of ways that any two values can be combined. And that's what we mean when we say that it's such a Swiss Army knife, because you can do whatever you want with those two values.
[00:03:58] What would you do if you had two functions? How might you combine two functions?
>> Kyle Simpson: A composition. We had somebody asked that earlier, as well. Compose and pipe. Those are fundamentally reductions. A list of functions that we compose is the same thing as a list of functions that we reduce where the reduction is composed the two items together and then compose two more, and compose two more.
[00:04:33] Call one, give its output is the input to the next. That's what it is. So our combined utility here needs to start within a list, arr and the reducer function. But we also typically will provide an initial value. That's important. We provide an initial value. Because sometimes your reduction since it's operating on two things, sometimes you need to have an initial value for one of those.
[00:04:59] And you therefore, start the reduction with the first item in the list plus the initial value. So, my reduction takes the first value in the list. The initial value and it combines them, and then goes to the second, and combines, and so forth. On the other hand, sometimes you don't need to provide an initial value at all.
[00:05:18] Because the first item in the list can be taken as the initial value and we can start the reduction with the second item on the list, and just start combining that way. You'll have both of those cases arise in your programming. Sometimes, you'll need an initial value and sometimes you won't.
>> Kyle Simpson: Some utilities will allow that to be optional. Some utilities require you to provide an initial value. And in those cases, if you have to provide an initial value, what you can do is actually pass in the first value of the list as your initial value and then start your reduction at position two.
[00:06:00] So, some utilities are smart enough to let that be optional and some aren't.
>> Kyle Simpson: Here, my combine against a list of numbers where I'm multiplying. I multiply 1 times 2 times 4 times 5. Do I need an initial value there for a multiplication of numbers?
>> Kyle Simpson: I use the initial value 1, but did I need to?
>> Kyle Simpson: There, it would have been sufficient for me to just multiply the numbers together without an initial value. My utility doesn't support that, cuz it's a simplified utility. But if my utility did make that one optional, I could have left it off. But here's an example of a reduction that requires an initial value.
[00:06:49] Cuz here, I wanna reduce with my acronym reducer. Each time it gets call, that's gonna be provided a string like a concatenated or a growing string and then a word to add on to it. But you'll notice I wanna take just the first character of each string and combine it in, cuz I'm making an acronym.
[00:07:08] What would happen if I left off the initial value of empty string down there? What would be the end result?
>> Speaker 2: You'd end up with undefined at the beginning of you string?
>> Kyle Simpson: No. If we left off the initial value, the default behavior is to start with the first item as the initial value which is the word Functional.
[00:07:37] And we'd start the reduction at the second one, which is the word Lights. So, what would our ending result string be?
>> Kyle Simpson: To be Functional LJS instead of FLJS, cuz we'd start with that. That would come in as string in our first called acronym and we keep that whole thing and then we take word.charAt which is the word Light, we'd be just taking L and starting to put it on.
[00:08:07] So, we just have functional LJS. So, here's an example where we really do legitimately need an initial value. The previous slide, we didn't need an initial value. You'll have both of those cases that you run across in your coding.
>> Speaker 2: If you were to set charAt one, would it give you the same thing as if you hadn't set the initial value?
[00:08:31] Would it still give you Functional LJS?
>> Kyle Simpson: CharAt is still gonna try to get the first, I mean, is gonna try to get a character or whatever position you asked for. So if you said charAt of one and you left the empty string, you'd end up with UIAT.
[00:08:49] The second character of each of those.
>> Speaker 2: I see. Yeah.
>> Kyle Simpson: And if you left off the initial value empty string, you would end up with Functional IAT. What's an example of reducing across a list of functions? How about composing functions together? What's an example of reducing across a list of promises?
>> Kyle Simpson: You could compose the promises together by chaining one promise onto the next one and onto the next one. And now you'd have a chain, a series of promises together. [COUGH] We're coming up on a break here, but I just wanna leave you with a thought before that break that these list operations.
[00:09:35] We've been illustrating them based on arrays. But really and truly, these are data structure operations. And so that all these concepts that we have been doing could be generalized to an object of properties or to a tree where your different values are nodes in the tree, or things like that.
[00:09:58] As a matter of fact, I have a whole section in my Functional Light JS book in the chapter on this operation. There's a whole section at the end which goes and implements map, produce, filter against a binary search tree where each item that you're visiting is a node in a tree.
[00:10:14] And it's gonna go depth first and through the tree doing in order universal through the tree, and so forth. So you can generalize these ideas to pretty much any data structure, but this is what I want you to contemplate over the break. Data structures don't always have to look like data structures.
[00:10:35] Sometimes the data structure that you're gonna operate on is your own code and I know that might sound awfully strange, but we are gonna be working on an exercise later towards the end of the course. That's gonna give you a chance to practice with that, so I just want you to chew on that.
[00:10:53] What would it mean to treat part of my code, the statements of my code as a data structure that I wanted to perform a set of operations against? What would that even mean? It sounds very meta, so I just want you to chew on that and we'll take this break.
[00:11:10] When we come back, we'll work on an excercise to practice a bunch of these list operations.