Week 4 - Arrays and Objects
JavaScript Array Objects
Section titled “JavaScript Array Objects”Array
s in JavaScript are simply numbered lists. A list of names, dates, numbers, or anything really. To create an Array we can use either the Array constructor function or write an Array literal.
//the Array constructorlet names = new Array('Jon', 'Bran', 'Rickon', 'Rob', 'Sansa', 'Arya');
//an Array literallet names = ['Jon', 'Bran', 'Rickon', 'Rob', 'Sansa', 'Arya'];
Arrays in JS are dynamically built and sized, meaning that you can change the number of elements inside one at any time. You do not have to provide a size limit when you create one.
To determine the number of items in an Array, use the length
property.
names.length; // returns 6
The values in an array do NOT all have to be the same datatype but it is considered to be a good practice. In other programming languages, like Dart, you will need to use the same datatype for each value in your list. Eg: all integers, all strings, all booleans, etc.
Viewing and Editing Arrays
Section titled “Viewing and Editing Arrays”If you want to see the value of a single element in an Array, then we use []
after the variable name with an index number inside it. Array elements are numbered starting at zero. The first element is number zero and the last element will be the length of the array less one.
names[0];// Jon (first element)names[1];// Bran (2nd element)// You can also use a variable as the index number toolet item = 3;names[item];// Rob (4th element)let last = names.length - 1;// 5names[last];// Arya
To change the value of any item in the array we use the same syntax with the square brackets and assign a new value to it.
names[0] = 'Tyrion';//changes element zero from Jon to Tyrion
names[6] = 'Reese';//adds a new element to the array at the 7th position
Introduction to Arrays
Array at method
Section titled “Array at method”A new method has been added recently for Arrays in JavaScript.
We already know that we can use the square brackets to target a specific element in an array. Inside the square brackets you put the index position of the element you want to access. Remember to start counting at zero.
let myArray = ['a', 'b', 'c'];console.log(myArray[0]); //aconsole.log(myArray[1]); //bconsole.log(myArray[2]); //c
Now, we can use the .at()
method to target elements in the same way.
let myArray = ['a', 'b', 'c'];myArray.at(0); //amyArray.at(1); //bmyArray.at(2); //c
So, what is the point of this new method if we could already do this with the square brackets?
We can now use negative numbers to count from the end of the array too.
myArray.at(-1); //cmyArray.at(-2); //bmyArray.at(-3); //a
Instead of having to find out the length of the array and then calculate the position, we can use negative numbers to represent the counting backwards from the end.
Array at Method
Pop, Push, Shift, Unshift
Section titled “Pop, Push, Shift, Unshift”If you want to add a new element to or remove an element from either the beginning or the end of the Array then we can use the pop
, push
, shift
or unshift
methods.
Picture the Array as a stack of Lego bricks. Element zero was the first one added and it sits at the bottom of the stack. The element on top was the last one added. The pop
method will remove an element from the top. The push
method will add a new element on the top.
The crude analogy used to remember the other two methods is - taking a shift
will drop an element off the bottom of the stack and unshift
will reverse that.
let removed1 = names.pop(); // remove the last element addedlet removed2 = names.shift(); //remove the first element from the array
names.unshift(removed2); //put removed2 back at the start of the Arraynames.push(removed1); //put removed1 back as the last element
You can call these methods repeatedly to remove everything from the Array, if you want.
Slice and Splice
Section titled “Slice and Splice”If you want to remove one or more elements from the middle of an Array you can use the slice
method. If you want to add one or more elements to the middle of an Array you can use the splice
method.
The slice
method needs a starting index and an ending point. The starting index is a number for the position of the first element to remove. The second number provided is the index to stop before. If negative numbers are used for either value then count from the end of the Array. If no ending number is provided then it will remove from the starting position to the end of the Array. The slice
method does NOT change the original array. It returns a copy of the selected items.
The splice
method can be used to only insert new elements or to remove elements and replace them with a new series of elements. It takes a starting position as the first argument. The second argument is how many elements to remove, which may be zero. The third and all subsequent arguments will be inserted at the starting index. The splice
method will change the original array.
let friends = ['Chandler', 'Phoebe', 'Julian', 'Bubbles', 'Rachel'];//elements 0, 1, 2, 3, 4
let start = 2;//start selecting atlet end = 3;//up to but NOT includinglet removedFriends = friends.slice(start, end);// slice() does not change the original array, it just returns the selected items// removedFriends is now Julian
start = 2;let numToRemove = 1;let more = friends.splice(start, numToRemove, 'Joey', 'Monica', 'Ross');//Bubbles removed and 'Joey', 'Monica', 'Ross' added//friends is now 'Chandler', 'Phoebe', 'Joey', 'Monica', 'Ross', 'Rachel'// splice() DOES change the original array
ForEach Method
Section titled “ForEach Method”Once you understand what arrays are and how you can add and remove values from an Array, then you need to know how to loop over the values. We already discussed the for...loop
and for...in
statements last week, and that works fine but there are actually built-in methods designed to loop over Arrays for specific purposes.
If you want to loop through an array, there is a built-in method for doing so, called forEach
. The method has one required parameter - a function. It will call this function once for each item in the Array.
When it calls the function, it will pass three things to the function.
- The item from the Array
- The index number of the current item.
- A copy of the Array which can be used to do comparisons.
The three items will ALWAYS come in that order. You can name the three variables whatever you like.
let beers = ['Corona', 'Headstock', 'Heineken'];beers.forEach(function (item, index, arr) { console.log(index, item);});/** outputs0 Corona1 Headstock2 Heineken**/
Array forEach Method
Array Map
Section titled “Array Map”The map
method works similarly to the forEach
method. It loops once for each item in the Array. It calls a function (which you provide) each time it loops. It provides the same three values to the function each time it is called.
The difference is that while the forEach
method returns undefined
, the map
method returns a NEW array built out of the return values from the function it called.
Because it called the function once for each item in the Array, it means that the new Array will always be the exact same length as the original array.
let cheeses = ['Gouda', 'Cheddar', 'Brie'];let newArr = cheeses.map(function (item, index, arr) { return item.substr(1, 2); //return the 2nd letter from item});console.log(newArr); // ['o', 'h', 'r']
// Here is the Arrow function equivalentlet newArray = cheeses.map((item) => item.substr(1, 2));
Array map Method
Array Filter
Section titled “Array Filter”Just like the forEach method and the map method, the Array filter
method will loop through an array and call a function once for each element in the array. The filter
method also returns a NEW array.
However, the difference between the map
and filter
methods is that the filter
method returns an array holding a copy of the original array elements, and it is allowed to include or exclude elements from the original array as it loops.
If the function called by the filter
method returns a falsey value then it excludes the current element. If it returns a truthy value then it will include that element in the new array.
let cast = ['Archer', 'Pam', 'Krieger', 'Cheryl', 'Mallory', 'Lana', 'Ray'];//we want to remove any name that is fewer than 6 characterslet newCast = cast.filter(function (item, index, arr) { if (item.length > 6) { return true; } //else the function will return undefined which is a falsey value});// the newCast array will be ['Archer', 'Krieger', 'Cheryl', 'Mallory']
//Here is the arrow function equivalentlet newCast = cast.filter((item) => item.length > 6);
Array filter Method
Sorting Arrays
Section titled “Sorting Arrays”At some point you will want to sort an Array. Thankfully, the JavaScript array object comes with a built-in sort
method. The sort
method will change the original Array.
The sort will sort the items in the Array in alphabetical order.
let names = ['Bob', 'Linda', 'Tina', 'Louise', 'Gene'];names.sort();console.log(names); //the sorted version will be logged
if you want to sort the Array in numerical order or in some other custom way, you can pass a function to the method which will be used to sort the values. Watch this video to learn more.
Array sorting
Custom Array sort
toSorted
Section titled “toSorted”Recently a new method was added to JavaScript - a toSorted()
method. It works ALMOST the same way as sort()
. You still can use it alone or pass it a function which will do the comparisons.
The difference is that it does NOT modify the original array. Instead it creates a copy of the original array which is sorted.
Here is the full MDN reference for the method - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted (⌘ + click) to open
reverse() and toReversed()
Section titled “reverse() and toReversed()”A couple other useful methods having to do with the order of the items in an Array are the array.reverse()
and array.toReversed()
methods. These can be used alone or in conjunction with the sorting methods.
They take the original order of the items in an array and reverse the order. It doesn’t do a sort, it just flips the array.
let arr = ['Sammy', 'Annie', 'Paulo', 'Janine'];let rev1 = arr.toReversed();//rev1 will be ['Janine','Paulo', 'Annie', 'Sammy']arr.reverse();// now arr will be ['Janine','Paulo', 'Annie', 'Sammy']
The difference between the methods is that reverse
changes the original array. toReversed
will create a copy of the array with the order of the items flipped.
Other Array Methods
Section titled “Other Array Methods”There are MANY array methods that are used routinely. Do not try to memorize them all. Get familiar with what the possibilities are. You will gain expertise in the methods over time, with practice. Revisit this list on a regular basis to review.
One thing to pay special attention to with Array methods is whether the method changes the original Array or whether it creates a new one. If the method changes the original Array then it is called a destructive method.
Array.concat( )
Section titled “Array.concat( )”The Array concat method allows you to add another Array on to the end of the original one. It returns a new Array.
let arr1 = [1, 2, 3];let arr2 = [4, 5, 6];let arr3 = arr1.concat(arr2); // [1,2,3,4,5,6]
Array concat method
Something else, which is a little bit more advanced, if you have an Array which contains other Arrays and you want to flatten all those contents into a single Array, then we can use the flat()
method.
let arr1 = [1, 2, 3];let arr2 = [4, 5, 6];let arr3 = [].concat(arr1, arr2);//now arr3 is an array with two items. Each item is an array.//to convert it to [1, 2, 3, 4, 5, 6] we can use flat()let arr4 = arr3.flat();
Later on we will talk about the flatMap()
method too.
Array.includes( )
Section titled “Array.includes( )”The Array includes method will loop through an Array looking for a match for the value that you provide. The value you provide can be any Primitive. Optionally you can add a starting position to begin looping. It returns a true
or false
value.
let arr1 = ['bob', 'mary', 'fred', 'alice'];let arr2 = ['Bob', 'Mary', 'Fred', 'Alice'];arr1.includes('Fred'); //falsearr2.includes('Fred'); //true
array includes
Array.some( )
Section titled “Array.some( )”The Array some method will loop through the values in an Array and returns true
as soon as it finds the first value that meets your condition. It accepts a function as it’s parameter. This makes it very efficient because it could exit before looping through all values.
let arr2 = ['Bob', 'Mary', 'Fred', 'Alice'];arr2.some((name) => { return name == 'Fred';}); //true
Array some Method
Array.every( )
Section titled “Array.every( )”The Array every method will check every value in an Array to see if they all meet a condition that you choose. It returns a true
or false
.
let nums = [1, 3, 5, 7, 9, 15, 23];//check if every number is oddnums.every((num) => { if (num % 2 == 1) { return true; } else { return false; }});
Array every Method
Array.join( )
Section titled “Array.join( )”The Array join method will convert all the values in the Array into Strings and then combine all the Strings into a single String. You have the option of putting a separator between each of the String values as their are joined. It returns a new string and does NOT change the original array.
let words = ['I', 'love', 'the', 'smell', 'of', 'napalm', 'in', 'the', 'morning'];words.join(' '); // "I love the smell of napalm in the morning"
String split and Array join
Searching in Arrays
Array.reduce( )
Section titled “Array.reduce( )”The Array reduce method will loop through an Array and return a single value. You provide a function and a starting value for comparison. The starting value is often called the accumulator.
//find the biggest number in an array that is positivelet nums = [-23, 45, -3, 0, 144, -42, 87];let big = nums.reduce((acc, num) => { if (num > 0 && num > acc) { return num; } else { return acc; }}, 0);
Array reduce Method
Reference
Section titled “Reference”Full Array Video Playlist (⌘ + click) to open
MDN Array Object Reference for all methods (⌘ + click) to open
JavaScript Objects
Section titled “JavaScript Objects”The Object
datatype is the base object that is used to create all other non-primitive data types in JavaScript. While this may sound quite abstract, you will actually be creating many Objects of your own.
There are a several ways to create objects. Here are a few of the more common ones.
//the object constructorlet myObj1 = new Object(); //creates an empty object
//an object literallet myObj2 = {}; //creates an empty object
//a Factory style function that creates objects when called with 'new' keywordfunction DoIt() {} //instead of returning undefined, it returns an object of type DoItlet myObj3 = new DoIt();
//The class syntax to define an object plus calling its constructorclass myApp { constructor() {}}let myObj4 = myApp();
The most common is the object literal syntax, where you are literally writing out what the object contains. Here is an object literal with 5 properties - 3 properties are just values (like primitive variables) and 2 are methods (like function expressions).
let potter = { age: 3189, //numeric property isWizard: true, //boolean property name: 'Harry', //string property speak: function () { console.log(`You're a wizard ${potter.name}.`); // potter.name is 'Harry' }, levitate: function () { console.log('Levi-oh-sa'); },};
7 ways to create objects
Prototypes
Section titled “Prototypes”Every type of Object has a prototype
. A prototype
is a special kind of an object that contains all the methods that will be shared by all Objects of that type.
JavaScript has something called the prototype chain
, which is how inheritance
works in JavaScript. Each one of the Object prototypes will have a connection to the prototype object belonging to it’s parent object. At the top of the chain is the prototype
of the Object
object.
As an example, look at the valueOf()
method. When you create an Array, there is no method in Array called valueOf
. However, you can write the following and no error occurs.
let letters = new Array('a', 'e', 'i', 'o', 'u');letters.valueOf();
valueOf reference (⌘ + click) to open
This works because of the prototype chain
.
We are calling the method Array()
. The Array
function has a prototype object. All the methods that you would call on your array, like map
or sort
or filter
are inside the Array.prototype
object. The prototype object of Array.prototype
is the Object.prototype
object. (this is the prototype chain)
When the line letters.valueOf()
is run, the JavaScript engine looks inside of letters
for a method called valueOf
. If it is not found then JS looks inside Array.prototype
for a method called valueOf
. If the method is not found there, then JS looks inside Object.prototype
for the method. Since Object.prototype.valueOf
does exist it can be run.
The prototype
of Object.prototype
is null
. Once null
is reached in the search through the prototype chain, then JS knows that it can safely say that an error has occurred.
If you ever need to get a full list of properties and methods inside an object, use the Object.getOwnPropertyNames()
method. The following code snippet shows how to get the properties from the Array object and its prototype.
let ArrayProps = Object.getOwnPropertyNames(Array);console.log(ArrayProps);let ArrayPrototypeProps = Object.getOwnPropertyNames(Array.prototype);console.log(ArrayPrototypeProps);
Prototype Chain Visually
We will talk more about the prototype chain in the future. For, now, it is enough if you understand that this is how inheritance of methods work in JavaScript and that every object has a prototype.
Square Brackets vs Dot Notation
Section titled “Square Brackets vs Dot Notation”The first way we access any property or method in an object is with dot notation. That means putting a period between each object name and property name.
let obj = { name: 'Bubba', age: 44,};obj.name; // has the value 'Bubba'obj.age; // has the value 44
There is an alternative syntax that uses square brackets.
let obj = { name: 'Bubba', age: 44,};obj['name']; // has the value 'Bubba'obj['age']; // has the value 44
Note the quotation marks around the property names. All Object property names (for our purposes) will be Strings.
So, why the two approaches? - With the square brackets we can put a variable inside the brackets instead of a string.
let obj = { name: 'Bubba', age: 44,};let n = 'name';let a = 'age';obj[n];// has the value 'Bubba'obj[a];// has the value 44
obj.n;// this would fail because JS would look for obj.n or obj['n']obj.a;// this would fail because JS would look for obj.a or obj['a']
So, if you are trying to dynamically update an object or access it’s properties inside a function and you need the function to work with any object or any property then you need to use variables that contain those references. With the square brackets we can reference any property.
function doUpdate(someObj, someProp, someVal) { //this function can update the value of any property inside any object someObj[someProp] = someVal;}
Dynamic Properties
Rules for naming variables and properties
Checking for Property Existence
Section titled “Checking for Property Existence”As a best practice, when you are going to change an object by updating the value of a property, deleting a property, or adding a new property, then you should check to see if that property already exists.
There are two ways that we can check for the existence of a property on any object. We can use the in
operator or the Object.hasOwn()
method. The in
operator is the simplest and shortest to write.
let obj = { depth: 8, width: 34, ref: 'AB393620',};if ('depth' in obj) { //there IS a depth property in the Object `obj`}if (Object.hasOwn(obj, 'width')) { //there IS a width property in the Object `obj`}
in Operator
Deleting Properties from Objects
Section titled “Deleting Properties from Objects”When you are done with a property and want to get rid of it there are two things we can do. First, to permanently delete the property, we use the delete
keyword.
delete obj['ref']; //removes the property `ref` from the Object `obj`.
delete Keyword
Now, if you only want to temporarily remove the value, but still keep the property to use later with a new value, we can just update its value to null
.
obj.myprop = null;obj['otherProp'] = null;//both these properties still exist but their value is now null.
Nested Loops for Complex Objects
Section titled “Nested Loops for Complex Objects”There will be times when you have a complex object that has arrays nested inside of arrays. When you need to loop through all of the elements at every level, we will use a nested loop.
Take this data as our example:
var pets = { animals: [{ dogs: ['Woody', 'Roxy', 'Rane'] }, { cats: ['Willow', 'Jesse Pink-kitty', 'Bob'] }],};
In this object, animals is an array of objects. Each of the objects inside of animals has a property which is an array.
We want to be able to write out all these pet names dynamically in a format that looks like this:
dogs Woody Roxy Ranecats Willow Jesse Pink-kitty Bob
So, a nested loop means a loop inside of a loop.
NOTE: In the outer loop we are declaring two variables a
and numAnimals
in the first part of the loop conditions. This is not something special for nested loops. You can do this with any for...loop
.
let animals = pets.animals; //our first outer arrayfor (let a = 0, numAnimals = animals.length; a < numAnimals; a++) { for (let prop in animals[a]) { //this will only loop once and get us the property name console.log(prop); //now get all the pet names for this prop (dogs or cats) for (let i = 0, numPets = animals[a][prop].length; i < numPets; i++) { console.log('\t', animals[a][prop][i]); //add the \t to write one tab space to the console } }}
Here we used a loop inside a loop inside a loop. The second loop was necessary because our array was hidden behind a property name. If we only had an array of arrays then two loops would be all that was required.
Nested Loop Examples
Objects, Primitives, and Memory in JavaScript
Section titled “Objects, Primitives, and Memory in JavaScript”Stack and Heap
Section titled “Stack and Heap”Stack and Heap are the names of two different places that values are saved for your JavaScript script.
When a JavaScript loads, some memory gets allotted to the script. That memory is split between two sections - the stack
and the heap
.
Primitive variables’ values get stored on the stack
.
Object values get stored on the heap
. References to these objects are held on the stack
.
As the script runs, each command in the current scope, that needs to run, will be added to the stack
. As each command is executed, it gets removed from the stack
.
If your script ever adds too many variables or commands to the stack
then it is possible to run out of allotted memory. When that happens, the script will stop running and you will get a Stack Overflow Error
.