Array, Set and ES2016
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
The Map
object is perfect for maps, or associative arrays as we call them in the
PHP biz. But what about true, indexed arrays? Well actually, JavaScript has always
had a great way to handle these - it's not new! It's the Array object.
Well, the Array object isn't new, but it does have a new trick. Let's check out
an example: when the page loads, we call loadRepLogs()
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
constructor($wrapper) { | |
// ... lines 9 - 11 | |
this.loadRepLogs(); | |
// ... lines 13 - 28 | |
} | |
// ... lines 30 - 39 | |
loadRepLogs() { | |
$.ajax({ | |
url: Routing.generate('rep_log_list'), | |
}).then(data => { | |
for (let repLog of data.items) { | |
this._addRow(repLog); | |
} | |
}) | |
} | |
// ... lines 49 - 170 | |
_addRow(repLog) { | |
// ... lines 172 - 175 | |
const html = rowTemplate(repLog); | |
this.$wrapper.find('tbody').append($.parseHTML(html)); | |
this.updateTotalWeightLifted(); | |
} | |
} | |
// ... lines 182 - 233 | |
})(window, jQuery, Routing, swal); |
This fetches an array of repLog
data via AJAX and then calls _addRow()
on each
to add the <tr>
elements to the table.
But once we add the table rows... we don't actually store those repLog
objects
anywhere. Yep, we use them to build the page... then say: Adios!
Now, I do want to start storing this data on my object, and you'll see why in a
minute. Up in the constructor
, create a repLogs
property set to new Array()
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
constructor($wrapper) { | |
// ... line 9 | |
this.repLogs = new Array(); | |
// ... lines 11 - 30 | |
} | |
// ... lines 32 - 184 | |
} | |
// ... lines 186 - 237 | |
})(window, jQuery, Routing, swal); |
If you've never seen that Array
object before... there's a reason - stay tuned!
Then, down in _addRow()
, say this.repLogs()
- which is the Array
object -
this.repLogs.push(repLog)
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
// ... lines 8 - 173 | |
_addRow(repLog) { | |
this.repLogs.push(repLog); | |
// ... lines 176 - 183 | |
} | |
} | |
// ... lines 186 - 237 | |
})(window, jQuery, Routing, swal); |
Back up in loadRepLogs()
, after the for
loop, let's see how this looks:
console.log(this.repLogs)
. Oh, and let's also use one of its helper methods:
this.repLogs.includes(data.items[0])
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
// ... lines 8 - 41 | |
loadRepLogs() { | |
$.ajax({ | |
// ... line 44 | |
}).then(data => { | |
// ... lines 46 - 48 | |
console.log(this.repLogs, this.repLogs.includes(data.items[0])); | |
}) | |
} | |
// ... lines 52 - 184 | |
} | |
// ... lines 186 - 237 | |
})(window, jQuery, Routing, swal); |
Obviously, this item should have been added to the Array
!
Refresh! Yea! We see the fancy Array
and the word true
. Awesome!
But hold on! The Array
object may not be new, but the includes()
function is
new. In fact, it's really new - it wasn't added in ES2015, it was added in ES2016!
ES2015 came with a ton of new features. And now, new ECMAScript releases happen
yearly, but with many fewer new things. The Array
' includes()
function is one of
those few things in ES2016. Cool!
Oh, and by the way, you don't typically say new Array()
... and PHPStorm is yelling
at us! In the wild, you just use []
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
constructor($wrapper) { | |
// ... line 9 | |
this.repLogs = []; | |
// ... lines 11 - 30 | |
} | |
// ... lines 32 - 218 | |
} | |
// ... lines 220 - 237 | |
})(window, jQuery, Routing, swal); |
That's right, when you create an array in JavaScript, it's actually this Array
object.
Calculating the Total Weight
But... why are we keeping track of the repLogs
? Because now, we can more easily
calculate the total weight. Before, we passed the Helper
object the $wrapper
element so that it could find all the tr
elements and read the weight from them.
We can simplify this! Instead, pass it our Array
: this.repLogs
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
constructor($wrapper) { | |
// ... line 9 | |
this.repLogs = []; | |
HelperInstances.set(this, new Helper(this.repLogs)); | |
// ... lines 13 - 30 | |
} | |
// ... lines 32 - 183 | |
} | |
// ... lines 185 - 236 | |
})(window, jQuery, Routing, swal); |
At the bottom of this file, change the constructor()
for Helper
to have a repLogs
argument. Set that on a repLogs
property:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 185 | |
/** | |
* A "private" object | |
*/ | |
class Helper { | |
constructor(repLogs) { | |
this.repLogs = repLogs; | |
} | |
// ... lines 193 - 217 | |
} | |
// ... lines 219 - 236 | |
})(window, jQuery, Routing, swal); |
Below in calculateTotalWeight()
, instead of using the $wrapper
to find all the
tr
elements, just pass this.repLogs
to the static function. Inside of that,
update the argument to repLogs
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 185 | |
/** | |
* A "private" object | |
*/ | |
class Helper { | |
// ... lines 190 - 193 | |
calculateTotalWeight() { | |
return Helper._calculateWeights( | |
this.repLogs | |
); | |
} | |
// ... lines 199 - 209 | |
static _calculateWeights(repLogs) { | |
// ... lines 211 - 216 | |
} | |
} | |
// ... lines 219 - 236 | |
})(window, jQuery, Routing, swal); |
Previously, _calculateWeights()
would loop over the $elements
and read the
data-weight
attribute on each. Now, loop over repLog of repLogs
. Inside,
set totalWeight += repLog.totalWeightLifted
:
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 185 | |
/** | |
* A "private" object | |
*/ | |
class Helper { | |
// ... lines 190 - 209 | |
static _calculateWeights(repLogs) { | |
let totalWeight = 0; | |
for (let repLog of repLogs) { | |
totalWeight += repLog.totalWeightLifted; | |
} | |
return totalWeight; | |
} | |
} | |
// ... lines 219 - 236 | |
})(window, jQuery, Routing, swal); |
It's nice to calculate the total weight from our source data, rather than reading it from somewhere on the DOM.
Okay! Try that out! The table still loads... and the total still prints!
Tip
Actually, we made a mistake! When you delete a rep log, the total weight will no
longer update! That's because we now need to remove the deleted repLog
from
the this.repLogs
array.
No problem! The fix is kinda cool: it involves adding a reference to the $row
element:
the index
on the this.repLogs
array that the row corresponds to. This follows
a pattern that's somewhat similar to what you'll see in ReactJS.
// ... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
// ... lines 4 - 6 | |
class RepLogApp { | |
// ... lines 8 - 73 | |
_deleteRepLog($link) { | |
// ... lines 75 - 83 | |
return $.ajax({ | |
// ... lines 85 - 86 | |
}).then(() => { | |
$row.fadeOut('normal', () => { | |
// we need to remove the repLog from this.repLogs | |
// the "key" is the index to this repLog on this.repLogs | |
this.repLogs.splice( | |
$row.data('key'), | |
1 | |
); | |
$row.remove(); | |
this.updateTotalWeightLifted(); | |
}); | |
}) | |
} | |
// ... lines 102 - 180 | |
_addRow(repLog) { | |
this.repLogs.push(repLog); | |
// ... lines 183 - 186 | |
const html = rowTemplate(repLog); | |
const $row = $($.parseHTML(html)); | |
// store the repLogs index | |
$row.data('key', this.repLogs.length - 1); | |
this.$wrapper.find('tbody').append($row); | |
this.updateTotalWeightLifted(); | |
} | |
} | |
// ... lines 196 - 247 | |
})(window, jQuery, Routing, swal); |
Introducing Set
But, ES2015 added one more new object that's related to all of this: Set
. It's
a lot like Array
: it holds items... but with one important difference.
Open up play.js
and set foods
to an array:
let foods = []; | |
// ... lines 2 - 7 |
Let's add gelato
to the array and tortas
. Clear everything else out:
let foods = []; | |
foods.push('gelato'); | |
foods.push('tortas'); | |
// ... lines 4 - 7 |
And ya know what? Gelato is so good, we should add it again. At the bottom, log foods
:
let foods = []; | |
foods.push('gelato'); | |
foods.push('tortas'); | |
foods.push('gelato'); | |
console.log(foods); |
When you run the script, there are no surprises: gelato
, tortas
, gelato
.
But now, change the array to be a new Set()
. To add items to a Set
, you'll use
add()
instead of push()
- but it's the same idea:
let foods = new Set(); | |
foods.add('gelato'); | |
foods.add('tortas'); | |
foods.add('gelato'); | |
console.log(foods); |
Try the script now.
Woh! Just two items! That's the key difference between Array
and Set
: Set
should be used when you need a unique collection of items. It automatically makes
sure that duplicates aren't added.
Oh, and there is also a WeakSet
, which has the same super powers of WeakMap
- all
that garbage collection stuff. But, I haven't seen any decent use-case for it.
Just use Set
... or Array
if values don't need to be unique.
I like this course but, why some videos do not have subtitles? ,thx