Transcript from the "Map: Transformation" Lesson
>> Kyle Simpson: All right moving right along let's talk about lists. And actually right off the bat I need to clarify we're not really only talking about arrays. Although these kind of operations are almost always illustrated using arrays, we are actually talking about data structures. The functional programming concept of doing operations is not limited to I can only do a map across an array or I can only do a filter across an array.
[00:00:51] Essentially what we are saying here is that there are operations that we've learned how to perform on single discrete values. And now what we'd like to is adapt those operations, lift those operations, so that they work across a collection of values. There's a scary sounding functional programming term here that is heavily related and it is called functor.
[00:01:17] A functor is a value, and by value I mean could be a collection of values, it is a value over which those values in it can be mapped. So if we talk about an array since we know we can call dot map on an array, it's a functor.
[00:01:35] In fact, any data structure for which we have identified and defined a map operation that constitutes or gives the characteristic that it behaves as a functor. And I only use that word to demystify these words, not because I want you to go around, start talking about functor this and functor that.
[00:01:55] But rather that if you ever hear that word, I don't want you to be scared by that word. You just need to think essentially that's a value that I can map an operation across, okay? So let's talk about map. What is map? It is fundamentally a transformation operation.
[00:02:12] Meaning that we're gonna to take the values in some collection and perform a conversion on each of them. And remember that a data structure doesn't imply necessarily multiple values. A single value in a single data structure can be that functor idea can be mapped over and that's still entirely valid.
[00:02:31] It's just that we often illustrated in terms of lists of values like I've depicted here. So the transformation, it's important to note that it's not happening in place. For it to be the functor, for it to behave according to these principles, it has to be a pure function.
[00:02:47] It can't be mutating this stuff. Your map transformation has to be producing a new data structure. With the values having been transformed as they are projected to the new data structure, okay? So if we start out with an array of four numbers, and then we map that to a new array of different numbers that have been added or subtracted from or something, that's a mapping operation.
[00:03:12] The key takeaway here mentally is that map always results in the same kind of data structure as it started with. You start with an array, map is gonna give you another array. You start with an object, map is gonna give you another object. If you start with a monad, map is gonna give you another monad, okay?
[00:03:30] The map always produces a transformed values but the same data structure, same kind, same shape of data structure, okay? That's a key takeaway. So how would we implement a very simple map transform kind of function for ourselves? Well, let's say that I had a list of strings, and I wanted to turn it into a list of objects where those strings have been included.
[00:03:55] I can have a function called makeRecord. And makeRecord is a pure function because it takes in a name and it produces a new function back. And I'm going to use asterisks on that pure, because we are technically doing an impurity with the unique random number generation or something.
[00:04:12] But from the perspective of passing in a value and getting back an object, it's returning a value rather than the mutating something for us, okay? So if I do a map over the array "Kyle" and "Susan" with makeRecord as our map function, you'll notice that what I'm basically saying is call mapper with every original element, and whatever it returns, stick it in the new data structure.
[00:04:38] That new less that we then return line ten, and that's how we end up with an array of objects. We have not mutated the original array of Kyle and Susan.
>> Kyle Simpson: Because arrays are the most common ways that people think about these operations, there is built in into the array prototype, the equivalent of these operations.
[00:05:00] So we have a .map on the array prototype. I can take that makeRecord and call it with my array directly with the array.map and use the mapper as MakeRecord. And we'll end up with the same kind of thing, an array with those kind of objects. So think about any sort of source value, any sort of conversion or transformation that you wanna do on that value to project the new value from it.
[00:05:25] We could take a number and map it with some sort of arithmetic operation and come up with a new number. We could take a string and map it with some kind of string concatenation, where we're prefixing or suffixing the string. We could take a string and map it with a two lowercase or a two uppercase transformation.
[00:05:44] In which case we're making all the strings uppercase. We could take a single promise and map it into a return promise. We could take a string and turn it into a promise. Any sort of source value with any sort of transformation can be mapped. And any collection of those values can be mapped.
[00:06:01] One thing that is also important to point out is that the idea of map, the idea of the other ones that we'll look at is that we are using the same transformation across all of the values in the data structure. So it's not the case that we do, some of them we map the numbers to strings, and some of them, we map the strings into promises, or whatever.
[00:06:22] That would be two separate distinct map operations across sub sections of our data structure. We pass in one mapper function and it does one set of things. It is possible for your mapper function to have some sort of if statement logic in it, some branching logic in it where it returns different things, but that's a sort of polymorphic way of thinking about it.
[00:06:43] And even though you might be able to do that I'm gonna strongly caution that a mapper function should do one thing. It should turn strings into numbers or strings into promises or functions into numbers or something like that instead of doing a bunch of polymorphic. And that's just a fancy way of saying branching based upon types.
[00:07:01] It shouldn't do a bunch of different things based on different input types. That's generally not a great idea. Again the key take away here is anytime I start out with a collection of values like an array or an object. A collection of values and I wanna perform a common operation for all of the values that transforms them into some other kind of value and come up with a new data structure.
[00:07:23] Then what I'm pattern matching in my head against is that's called a map.