Array & Object Prototype Methods

Module 2 | JavaScript » Data Types

Lesson Goals

Students should be able to:

  • define what an array is; define what an object is
  • articulate the difference between arrays and objects, and understand when you would use each one
  • define what a prototype method is and what it allows you to do
  • list several prototype methods for both arrays and objects
  • understand how to manipulate arrays and objects, and the use-cases for different prototype methods

Arrays & Objects

Arrays and objects are the more complex data types that are available to us in JavaScript. What are all the data types you know of?

In Your Notebook

Write down everything you know about arrays. Everything you know about objects. Think about when you've used them in the past, where you've seen them, and what you've done with them. What do people mean with they say that "everything is an object" in JavaScript?

An array is essentially a comma-separated list of related values. For example:

  
    let pizzaToppings = ['onions', 'garlic', 'peppers'];
  

An object is a set of data stored in key-value pairs that provide a little additional specificity and context to the information. For example:

  
    let pizza = {
      crust: 'thin',
      sauce: 'tomato',
      size: 14,
      extraCheese: true,
      toppings: ['onions', 'garlic', 'peppers']
    };
  

Prototype Methods

In order to understand what a prototype method is, let's break down the terms individually. A prototype is a model of something and how it should look or behave. We can build off of prototypes to create multiple copies of similar models. A method in JavaScript is simply a function that's defined on an object.

Prototype methods are functions that allow you to manipulate the value of a particular data type or class. JavaScript comes with several built-in data types that each have their own prototype methods, that allow you to interact with them in certain ways. For example, you might want to add or remove an item from an array. Or inspect the properties on an object. Prototype methods allow you to perform these actions and manipulate your values.


Objects

Objects allow you to define a set of related information using key-value pairs. The 'keys' (also known as 'properties') are strings that describe the value associated with it. You can think of object properties as variables that are attached specifically to that particular object.

Object.keys(objectName)

  • Purpose: iterating over each property of an object
  • Potential Use Case: appending each property and its value to the DOM
  • Parameters: an object whose keys you want access to
  • Returns: an array of the keys on the object passed in

Object.keys() is a method that will return an array of all the properties for the object you pass in. For example:

  
    let pizza = {
      crust: 'thin',
      sauce: 'tomato',
      size: 14,
      extraCheese: true,
      toppings: ['onions', 'garlic', 'peppers']
    };

    let pizzaProps = Object.keys(pizza);
    console.log(pizzaProps);
    // ['crust', 'sauce', 'size', 'extraCheese', 'toppings']
  

Object.assign(targetObject, ...sourceObjects)

  • Purpose: making a copy of a pre-existing object, merging two or more objects together
  • Potential Use Case: creating a new object based on pre-existing objects
  • Parameters: a target object (where the new object will exist), unlimited source objects (pre-existing objects that you might want to merge)
  • Returns: the target object

The Object.assign() method can be used to copy all the values from one or more objects to a brand new object - called a target object. It can be used for creating a fresh copy of a single object, or for combining properties of multiple objects. The method will return the target object.

Copying an Object

Sometimes we want to make a copy of a pre-existing object. It might seem simple enough to do something like this:

  
    let brittanysPizza = {
      crust: 'thin',
      sauce: 'tomato',
      size: 14,
      extraCheese: true,
      toppings: ['onions', 'garlic', 'peppers']
    };

    let pamsPizza = brittanysPizza;
  

But this actually has a major pitfall. When we assign objects to new variables, we're not truly creating a new, independent copy of the object. We're simply telling our new variable (pamsPizza) to reference the pre-existing object (brittanysPizza). So now there are two variables pointing to the same exact object in memory. This becomes problematic if we decide we want to update one of the pizza objects, but not the other. For example:

  
    pamsPizza.sauce = 'vodka';
    console.log(brittanysPizza.sauce); // 'vodka'
  

A safer way to make a true, independent copy of a pre-existing object is to use Object.assign():

  
    let brittanysPizza = {
      crust: 'thin',
      sauce: 'tomato',
      size: 14,
      extraCheese: true,
      toppings: ['onions', 'garlic', 'peppers']
    };

    let pamsPizza = Object.assign({}, brittanysPizza);

    pamsPizza.sauce = 'vodka';

    console.log(pamsPizza.sauce); // 'vodka'
    console.log(brittanysPizza.sauce); // 'tomato'
  

The Object.assign() method takes in an indefinite amount of arguments. The first argument must be your target object. When we're simply copying a pre-existing object, our target object will be an empty object. The second parameter is the source object. Or, whatever object you're hoping to copy properties from. You can pass in as many source objects as you want.

Merging Multiple Objects

Sometimes you might want to combine the properties from multiple objects into a single, new (target) object. This is also a scenario that Object.assign() can handle for you. One example is when you want to apply instance properties to a Class in a quick, clean way. You may have often seen instance properties defined on a class like so:

  
    class Person {
      constructor(name, age, height, weight) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
      }
    }
  

Imagine all the instance properties we might want to add to this Person class. That could be tons of lines of repetitive code. With Object.assign(), however, we can clean this up a bit by doing something like:

  
    class Person {
        constructor(name, age, height, weight) {
            Object.assign(this, { name, age, height, weight });
        }
    }
  

While this particular example is just giving us some nice, syntactic sugar, there are many other scenarios where you might want to merge objects. Let's practice!

Practice

Let's assume the user has reviewed the order, and they decide to click the 'Place Order' button. When they click that button, we want to add additional information to their order details and display those on the DOM. Follow the instructions listed in this gist.


Arrays

We mentioned earlier that arrays are comma-separated lists of related values. MDN defines arrays as "high-level, list-like objects". What they mean by this, is that arrays are actually objects under the hood. Think back to when we logged the result of Object.keys() to the console. You could see that it was an array, but each list item was numbered, starting at zero. Arrays are specialized objects where the keys are ordered numbers.

Arrays act as kind of an abstraction over objects. Think about how simple the syntax is for creating a new array:

  
    let toppings = ['pineapple','cheese','garlic'];
  

If JavaScript didn't provide us with this simplified syntax for creating arrays, we would have to define them like we do objects:

  
    let toppings = {
      0: 'pineapple',
      1: 'cheese',
      2: 'garlic'
    };
  

Not only is this more verbose and harder to write, the numbered keys in this object really don't provide us with any useful information or context about each value. JavaScript abstracts this away from us by providing us with a specialized Array data type.

Note: Try running Object.keys() against an array. You'll see that this method still works because arrays are objects under the hood! You should get back an array of ordered numbers.

The array data type comes with its own set of prototype methods for manipulating and accessing its data. There are tons of them, and sometimes picking which ones to use can be tricky. There are three main categories of array prototype methods: mutator, accessor, and iteration methods. While it's not vital that you remember the names of these categories, knowing what each set does can help you narrow down your choices when trying to determine what method to use to perform a particular action.

Mutator Methods

Directly modify the existing array

Mutator methods can be used for adding or removing items from an array, sorting the array, reversing it, etc. They modify the existing array and might return the modified array, or the single array item that was affected.

As an example, one of the mutator methods you can call on an array is sort(). The sort() method will sort strings alphabetically and integers numerically by default:

  
    let instructors = ['Leta', 'Robbie', 'Brittany'];
    instructors.sort(); // ['Brittany', 'Leta', 'Robbie'];
    console.log(instructors); // ['Brittany', 'Leta', 'Robbie'];
  

Accessor Methods

Do not modify the existing array; return a particular representation of the array

Some accessor methods are very similar to the mutator methods, but the big difference here is that they do not modify the original array, and instead return a copy of it that reflects whatever mutation you made.

One example of an accessor method is join(). We can use this method to join together all the items of an array and return the resulting string:

  
    let lessons = ['module2', 'javascript', 'arrays'];
    lessons.join('-'); // module2-javascript-arrays
    console.log(lessons); // ['module2', 'javascript', 'arrays']
  

Practice

In pairs, take a look at the Array prototype methods listed here. We'll practice specifically with slice, splice, pop, shift, push, unshift. Create a repl or in your browser dev tools console, play with the array method you're assigned. Pay particular attention to the following:

  • the original array before you call a method on it
  • what the method does
  • what the method returns
  • the value of the original array after calling your assigned method

We'll have each group demo and explain their assigned method.

Iteration Methods

Loops through the existing array and applies a callback function that may mutate each element and return a new value

Iteration methods generally take in a callback function as their first argument.

Array.map(callbackFunction)

Use Case: when you want a new array based on your original, with some modification to each item

The map() method is very similar to forEach(), except that each time the callback is executed, whatever is returned from the callback is added to a new array that map returns.

  
    let evenNumbers = [2, 4, 6, 8, 10];

    let oddNumbers = evenNumbers.map((number, index, array) => {
      return number + 1;
    });

    console.log(oddNumbers); // [3, 5, 7, 9, 11]
  

Array.find(callbackFunction) and Array.filter(callbackFunction)

Array.find() helps you find a particular item in an array that matches a given condition. It will return the very first array element where the callback function returns true, even if there are multiple matches.

  
    let pets = [
      { name: 'harvey', age: 1 },
      { name: 'julius', age: 3 },
      { name: 'mishu', age: 3 },
    ];

    let threeYearOldPup = pets.find(pet => {
      return pet.age === 3
    });

    console.log(threeYearOldPup); // { name: 'julius', age: 3 }
  

Array.filter() is very similar to Array.find(), but instead of simply returning the first match, it will return a new array with all elements that match.

  
    let pets = [
      { name: 'harvey', age: 1 },
      { name: 'julius', age: 5 },
      { name: 'mishu', age: 5 },
    ];

    let adultPets = pets.filter(pet => {
      return pet.age === 5;
    });

    console.log(adultPets);
    // [{name: 'julius', age: 5}, {name: 'mishu', age: 5}]
  

Array.reduce(callbackFunction, initialValue)

Array.reduce() can turn an array into a single value. This single value could be a number, string, object, or another array. To accomplish this, reduce takes in two parameters:

  • a callback function - Within the callback, we have access to the accumulator, the current element in the iteration, the current element's index, and the original array we are looping over
  • an initial value - The initial value to be used as the accumulator (the first argument to the first call of the callback). The accumulator is the 'single value' that will eventually be returned. It's called an accumulator because each iteration over the array will modify the accumulator value until the loop is complete.
  
    const numbers = [1, 2, 3, 4, 5];

    let sum = numbers.reduce((sum, number) => {
      sum += number;
      
      return sum;
    }, 0);
  

There is a lot going on in reduce, so let's take a look at another example. Imagine we have a really complex data structure that is an array that contains multiple arrays, and we want to flatten it into a single array. We could do something like the following:

  
    let messyArray = [[0, 1], [2, 3], [4, 5]];

    let cleanArray = messyArray.reduce(
      (accumulator, currentElement) => {
        return accumulator.concat(currentElement);
      },
      []
    );

    // cleanArray is [0, 1, 2, 3, 4, 5]
  

Checks for Understanding

  • What is an array?
  • What is an object?
  • What is a prototype method?
  • Name three prototype methods for an object.
  • Name three prototype methods for an array.
  • When might you use Object.assign()?
  • What do accessor methods help you do with arrays? Give an example.

Further Reading & Resources