Skip to content

Week 4 - Arrays and Objects

Arrays 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 constructor
let names = new Array('Jon', 'Bran', 'Rickon', 'Rob', 'Sansa', 'Arya');
//an Array literal
let 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.

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 too
let item = 3;
names[item];
// Rob (4th element)
let last = names.length - 1;
// 5
names[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

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]); //a
console.log(myArray[1]); //b
console.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); //a
myArray.at(1); //b
myArray.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); //c
myArray.at(-2); //b
myArray.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

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 added
let removed2 = names.shift(); //remove the first element from the array
names.unshift(removed2); //put removed2 back at the start of the Array
names.push(removed1); //put removed1 back as the last element

You can call these methods repeatedly to remove everything from the Array, if you want.

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 at
let end = 3;
//up to but NOT including
let 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

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.

  1. The item from the Array
  2. The index number of the current item.
  3. 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);
});
/** outputs
0 Corona
1 Headstock
2 Heineken
**/

Array forEach Method

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 equivalent
let newArray = cheeses.map((item) => item.substr(1, 2));

Array map Method

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 characters
let 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 equivalent
let newCast = cast.filter((item) => item.length > 6);

Array filter Method

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

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

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.

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.

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.

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'); //false
arr2.includes('Fred'); //true

array includes

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

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 odd
nums.every((num) => {
if (num % 2 == 1) {
return true;
} else {
return false;
}
});

Array every Method

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

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 positive
let 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

Full Array Video Playlist (⌘ + click) to open

MDN Array Object Reference for all methods (⌘ + click) to open

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 constructor
let myObj1 = new Object(); //creates an empty object
//an object literal
let myObj2 = {}; //creates an empty object
//a Factory style function that creates objects when called with 'new' keyword
function DoIt() {} //instead of returning undefined, it returns an object of type DoIt
let myObj3 = new DoIt();
//The class syntax to define an object plus calling its constructor
class 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

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.

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

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

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.

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
Rane
cats
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 array
for (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 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.