ES2019 introduces the Array.prototype.flat method. In this lesson, we'll see how to flatten an array using Array.prototype.reduce and Array.prototype.concat, and then refactor the code to do the same thing using the .flat method.
Instructor: [00:00] Now we're looking at our Nuxt.js application and seeing how we generate averages for students. The input file, scores.json, lists out the students' names, their identifier, their teachers' names, and a list of their scores.
[00:14] However, this is not just a simple array. It's actually a nested array in that it contains scores achieved on any given day and an array of multiple tests that were taken on the same day. This value for Alicia represents on day one a score of 97, day two a score of 93. On day three, two tests were taken, one with a score of 93, one with a score of 97.
[00:34] In our Nuxt.js application, we're again iterating through each of the sorted records and outputting their average as their score. Therefore, we need to again look at our sorted computed property in order to see how average is created.
[00:49] We see that we take each record. We're going to map it and transform each record. What does transform do? Transform takes the scores the student has achieved, flags them, and then takes the average of them.
[01:03] Average is a simple utility that I wrote which takes in a flat array and computes the arithmetic sum of those values and then divides that sum by the length. That is, if there was an array of one, two, and three, it would first iterate over this array.
[01:19] Taking in zero as the first A, one as the first B. Adding them together, getting one. Returning that to the reducer method. The next iteration would take that one. Add two to it to get three. Then take that three. Add three to it to get six. Sum of six, length of three, average is two.
[01:37] This function requires a flat array however. We just saw that our array is not flat. That is, our array has a nested array inside of it. Prior to ES2019, you'd need to rely on something like lodash.flatten or write your own flat function.
[01:52] Flat may look intimidating because it's a recursive function in that it can deal with arrays of arbitrary depth. If we take our scores and paste them over here as a comment for reference, we could see how this operates.
[02:05] If we passed this array in as arr, with a depth of two, we see that the output array is initialized to an empty array. For each element of the input array, we see if that element itself is an array and, if so, push the result of flattening the subarray onto our overall array.
[02:24] Otherwise, we simply push the value. That is, operating on these values first it would push 97 onto our output array. I'll annotate that here. For the next record, we'd see that again it's not an array. It would push 93. Our next element would be an array.
[02:43] Then we say let's push the result of calling flat with the subarray of 93 and 97 with a depth less than two, which is a depth of one. This would then recurse. It would pass in the array 93 and 97 to flat. It would instantiate a flat array of empty array.
[03:04] It would go through each value, first encountering 93 and pushing that onto flat array, then encountering the value of 97 and pushing that onto flat array, and finally returning that flat array. It would return these values back out to the result of this function.
[03:21] This essentially becomes push 93, 97. If you're not familiar with the spread operator, this essentially says given an array, turn those into arguments to this function. This essentially becomes push 93 and 97, which will push 93 and 97 onto our resulting array. Our output would be 97, 93, 93, 97.
[03:57] Thankfully, ES2019 comes with a built-in function called flat that'll do that for us. We can essentially just delete this function. Save the file. Reload the page. We see that it hasn't changed.
since there's no reason the code couldn't keep recursing as long as some value were still an array
Infinite depth is not a safe default in the case of a self-referencing nested array, and also most nested arrays encountered in the wild aren't arbitrary depth. So that's why 1 was chosen as the default depth.
This is cool. Nice to see such a standard operation codified.
I was just going to ask why you needed to pass a depth at all—especially if you might not know it—since there's no reason the code couldn't keep recursing as long as some value were still an array, but don't worry, the method is already on it (from MDN):
Quick side-note: I'm not sure the lesson description text is right. It says "we'll see how to flatten an array using Array.prototype.reduce and Array.prototype.concat," but
reduce
is only used to sum the flattened array, andconcat
isn't used at all that I could spot.