MyLabel: ...JavaScript.skills.flat(Infinity)
MyLabel: ...JavaScript.skills.flat(Infinity)
In my previous article, I forgot some important things I was gonna explain: Array.flat()
and ...
for arrays. And now I’ll even add MyLabel:
for your own sanity!!
Array.flat()
To start learning about arrays, let’s make a simple one:
const array = [
1,
2,
[
3,
1000000
]
]
Now, this would be pretty great on its own, but what if we wanted to loop over every value in the array?
We would start with a simple for... of
loop:
for (const item of array) {
console.log(item)
}
Now our console would show this:
1
2
[3, 1000000]
But looking back at that sentence, this isn’t literally EVERY value in the array. The values in the “nested” array (an array nested inside of an array, use context clues there) are still not looped over.
This is where Array.flat()
comes in.
With Array.flat()
, all we need to do is to supply the levels of nesting to lift the values out of the arrays it finds there on that level:
// ...
const refinedArray = array.flat()
// ...
This may look a bit confusing, but by default, that value is 1
.
Now, if we repeat that same loop from before again, we’ll get this correctly:
1
2
3
1000000
And if we have even more arrays nested inside of nested arrays, we can increase the number.
But what if we are constantly updating array
?
Then it gets a little tricky. Your first thought would be to get something like array.highestNestingLevel
, right? Now, something like that would be cool, but sadly, it doesn’t exist.
This means you would have to calculate this value yourself, which would be a real pain.
But there’s a better way.
What if we pass Infinity
to Array.flat()
? Now, it might sound just as dumb as questioning if array.highestNestingLevel
exists in the complicated world of JavaScript, but Infinity
actually works - and exactly how we expected it to!
const array = [
1,
2,
[
3, // Editing
[
1000000,
1000000000, // Editing
[
"handspeak",
[ // Editing
"nintendo",
"speedothreesixty",
"blog",
"webdevsimplified",
"blog"
] // More editing
] // EDITING EVERYWHERE!!!!
]
]
] // Who the heck knows how much nesting there is here?!? Millions of people are updating this in commits, and if only one person is, they aren't gonna update the number everytime to test!
const refinedArray = array.flat(Infinity) // :O Heck???
for (const item of refinedArray) { // Testing lines
console.log(item) // Testing lines
} // Testing lines
Okay, I know those comments were a bit… Melodramatic, but they’re still mostly true!!!
Now, I’m not gonna go through and show you all of the values in the array, but I’ll tell you JavaScript WILL do it!!
Array.map()
Now, I didn’t include this in the initial list, but since I explained Array.flat()
, I’m gonna explain its buddy: Array.map()
. In fact, they’re such best buddies that JavaScript developers added a feature where they go hand-in-hand which I’ll explain later.
Let’s make a people
array of objects:
const people = [
{ name: "Bob", favoriteNumbers: [ 350, 1, -998001, 998001, -Infinity, Infinity ] },
{ name: "Speedo", favoriteNumbers: [ 360, 998001, -Infinity, Infinity ] },
]
Now what if we wanted both Bob’s and Speedo’s favorite numbers into one array, with each number only appearing once?
Array.map()
allows us to loop over each item in an array and then turn that into an array of a specific return value. Conventionally, the item’s data should be used somewhere for the return value.
Let me explain.
For our people
array, let’s run Array.map()
on it:
// ...
people.map()
The next step is to create a function that gives us the data we want.
// ...
people.map(p => /* ??? */)
What do we want?
We want favoriteNumbers
, so let’s move on.
// ...
people.map(p => p.favoriteNumbers)
Finally, we set that to be the value of some variable.
// ...
const favoriteNumbers = people.map(p => p.favoriteNumbers)
But if we run this, we get:
[[350, 1, -998001, 998001, -Infinity, Infinity], [360, 998001, -Infinity, Infinity]]
I already see two things wrong with this:
favoriteNumbers
is two arrays, since we just combined them together.998001
,-Infinity
, andInfinity
are duplicated.
We can fix problem 1 by running .flat()
on the result:
// ...
const favoriteNumbers = people.map(p => p.favoriteNumbers).flat() // Combine favorite numbers together
Now our array looks like this:
[350, 1, -998001, 998001, -Infinity, Infinity, 360, 998001, -Infinity, Infinity]
Now the duplicates are obvious.
The way to fix these is to use the new ES6 Set
:
// ...
let favoriteNumbers = people.map(p => p.favoriteNumbers).flat() // Combine favorite numbers together
favoriteNumbers = new Set(favoriteNumbers) // Remove duplicates
favoriteNumbers = [ ...favoriteNumbers ] // Convert the Set back into an array
And now we (finally) have this result:
[350, 1, -998001, 998001, -Infinity, Infinity, 360]
Array.flatMap()
For the specific case above, we didn’t have to use .flat(Infinity)
. And so JavaScript developers made Array.flatMap()
for the specific case above.
Now we can write the solution like this, as well:
// ...
let favoriteNumbers = people.flatMap(p => p.favoriteNumbers) // Combine favorite numbers together
favoriteNumbers = new Set(favoriteNumbers) // Remove duplicates
favoriteNumbers = [ ...favoriteNumbers ] // Convert the Set back into an array
...
for Arrays
In my last example, I used the spread operator (...
).
What the heck does that mean?!?
To understand this, there is a side effect of you learning array destructuring, another part of ES6 just like ...
.
OK? OK.
First, More, Last
Let’s say we magically had an array of YouTube comments on a video:
const comments = [
"First :)",
"Second",
"LOL",
"Hi!",
"S U B B I N G T O W H O E V E R L I K E S T H I S C O M M E N T A N D S U B S M E B A C K",
"Ban me from your channel",
"Go die",
"Last :(",
] // const allows Array.push(), by the way :) <3
What if we wanted to get the first and last comments if the array was actually a jumbled mess with classes with loads more properties that bury the message itself, as well as an array of all the other comments for good measure??
This is where array destructuring comes in.
Let’s do just that!
const [ first, ...more ] = comments
This uses brackets ([]
) to indicate destructuring, then has first
. Since this is the first variable, it has the first comment. ...more
is prepended with ...
, so it is everything else past first
.
The next thing to do would be to remove the last comment from more
:
const last = more.pop()
more.pop()
removes the last element of itself, which is the last comment, and sets that to the value of last
.
Now we can log our variables out:
console.log(`first=${first}`) // first="First :)"
console.log(`more=${more}`) // more=["Second", "LOL", "Hi!", "S U B B I N G T O W H O E V E R L I K E S T H I S C O M M E N T A N D S U B S M E B A C K", "Ban me from your channel", "Go die"]
console.log(`last=${last}`) // last="Last :("
Another amazing thing to note is that comments
is unchanged:
console.log(comments) // ["First :)", "Second", "LOL", "Hi!", "S U B B I N G T O W H O E V E R L I K E S T H I S C O M M E N T A N D S U B S M E B A C K", "Ban me from your channel", "Go die", "Last :("]
Concatenation
Another less-useful feature of ...
is a more readable syntax than:
array1.concat(array2).concat(array3).concat(array4).concat(array5)
By using ...
:
[ ...array1, ...array2, ...array3, ...array4, ...array5 ]
True Clones
Due to pass-by-reference, if I make a clone of an array, I will be editing the cloned array everytime I edit that cloned array.
For example:
const a = [1, 2, 3]
const b = a
b.push(4)
// a = [1, 2, 3, 4]
// b = [1, 2, 3, 4]
These are NOT true clones, and can cause a lot of bugs in everyone’s code.
Again, this is where array destructuring comes in.
Let’s do the exact same code with ...
:
const a = [1, 2, 3]
const b = [ ...a ]
b.push(4)
// a = [1, 2, 3]
// b = [1, 2, 3, 4]
Array-Like Objects Growing Up
This was used in the previous example <3.
Let’s imagine we have an ES6 Set
:
const mySet = new Set([
{ name: "Sally", favoriteNumber: 3 },
{ name: "Billy", favoriteNumber: 5 },
])
But now what if we want to map
over the Set
?
const favoriteNumbers = mySet.map(p => p.favoriteNumber) // Uncaught TypeError: mySet.map is not a function
This is because a Set
is not a typeof array
, and in fact a typeof object
, since it is an instanceof Set
, a class. Remember, classes are objects.
However, destructuring is so smart it can convert a Set
into a normal array:
const myArray = [ ...mySet ]
const favoriteNumbers = myArray.map(p => p.favoriteNumber) // [3, 5]
This is the trick we used for the YouTube comments: Convert it into a Set
, then convert it back into an Array
. Just so you know, Set
removes duplicates.
(This will link to ES6 Set
s once I create the blog post for them.)
(This will link to ES6 Map
s once I create the blog post for them.)
Destructuring Returns
Let’s make an almost-useless function that takes an object with two properties - a
and b
- and returns those values of theirs:
function twoProperties({a,b}){
return [a,b]
}
This uses object destructuring (will link to blog post once I create it) to get the values of the properties a
and b
of an object provided.
Array destructuring can help split these into specific variables.
Let’s do this!
const object = { a: "Apple", b: "Butt" }
const [ a, b ] = twoProperties(object)
// a = "Apple"
// b = "Butt"
This creates an object wherein a
is "Apple"
and b
is… "Butt"
.
MyLabel:
Labels are most useful for loops, so let’s use those.
const array1 = [1, 2, 3]
const array2 = [ ...array1, 4, 5, 6 ]
for (const i of array1) {
if (i === 2) {
for (const j of array2) {
if (j === 6) {
console.log("HA!")
break
} else { console.log("HO!") }
}
} else { console.log("HEY!") }
}
What you’ll get is this:
"HEY!"
"HO!"
"HO!"
"HO!"
"HO!"
"HO!"
"HA!"
Except that’s not what you’ll get.
You’ll actually get this:
"HEY!"
"HO!"
"HO!"
"HO!"
"HO!"
"HO!"
"HA!"
"HEY!"
This is because we are only break
ing the second loop, which means for the last element of array1
, 3
, we’ll get an icky, lingering "HEY!"
.
To fix this, we can literally LABEL the loop! (If you’re like me, you’ve seen in your text editor “Unused label” at some point when you’ve messed around with your code. I was completely bemused until WebDevSimplified saved me.)
Let me show you:
const array1 = [1, 2, 3]
const array2 = [ ...array1, 4, 5, 6 ]
loop: for (const i of array1) {
if (i === 2) {
for (const j of array2) {
if (j === 6) {
console.log("HA!")
break loop
} else { console.log("HO!") }
}
} else { console.log("HEY!") }
}
/*
"HEY!"
"HO!"
"HO!"
"HO!"
"HO!"
"HO!"
"HA!"
*/
This labels the main loop as loop
. Then our break
statement is linked to loop
, which contains our second loop, too. So when we reach our "HA!"
, it breaks everything down.
Conclusion
These topics are not taught in commonplace and are essential for you to even look like a developer. Soon, if you learn to think like a developer (which I probably will explain in more essay-based detail in another, far-in-the-future blog post), your code will start to look professional and actually look easy-to-read for anyone, especially by using small functions that have understandable names for everyone with all of this complex code still being compact in them.
If you have any additional ideas for blog posts, please email me at deanlovesmargie@gmail.com. &&
- with that out of the way - console.log("farewell, and have a great day!")
.
< JavaScript Basics | How to Gamble Food > |