Lesson Weekend

We can now add a Contact to our AddressBook. However, this feature isn't very helpful unless we can also retrieve a contact later. This feature is necessary for any application that uses a real database - it's just as important to be able to retrieve data from the database as it is to store it. In this lesson, we'll create an AddressBook.prototype.find() method that allows us to find a Contact by its id property.

Finding Contacts

This method will actually be really easy to write - and very efficient. Here it is:

scripts.js
...

AddressBook.prototype.findContact = function(id) {
  if (this.contacts[id] != undefined) {
    return this.contacts[id];
  }
  return false;
};

...
  • First, our new method takes an id as an argument. This will contain the unique ID we assigned to each Contact in the last lesson.

  • Next, we use a conditional to check if this.contacts[id] != undefined. If we try to find a property in an object that doesn't exist, JavaScript will return undefined. We don't want our method to return undefined, though. We want it to return a specific Contact. If that Contact doesn't exist, we'll return false.

  • So if this.contacts[id] isn't undefined, we'll return that specific contact (this.contacts[id]). There's no need to iterate through the object. If our AddressBook mock database were an array, we'd have to loop through each of the contacts until we find a matching ID - which isn't efficient at all. Finding a value by its key in a JavaScript object means it's super-fast to look up a contact - no matter how big the address book gets.

By the way, here's an interesting fact - under the hood, JavaScript stores all the properties of an object in an array. So technically, we are kind of using an array to look things up. However, JavaScript uses a very efficient hashing algorithm so that each key in an object corresponds to an index in an array. It does this because it's very fast to find an element in an array based on its index. This is more advanced stuff which we cover in our Hash Tables computer science curriculum. We don't recommend looking at this lesson yet as it's more advanced, but later in the program, you'll have an opportunity to learn more about this.

If the conditional isn't met, that means the contact doesn't exist in the database and the method will return false.

Testing it Out

Let's test our new method out. Like before, we'll copy/paste the contents of scripts.js into the browser's JavaScript console. Next, enter the following in the console to populate our AddressBook:

let addressBook = new AddressBook();
let contact = new Contact("Ada", "Lovelace", "503-555-0100");
let contact2 = new Contact("Grace", "Hopper", "503-555-0199");
addressBook.addContact(contact);
addressBook.addContact(contact2);

Then we'll confirm our code provided each Contact an id by entering the following:

contact2;

The console will respond with the Contact object that corresponds to the variable contact2:

Contact {firstName: "Grace", lastName: "Hopper", phoneNumber: "503-555-0199", id: 2}

Once again, notice our Contact object has an id property even though the constructor didn't assign this property. We could update our constructor to assign an ID right away but it's not necessary to do so. Constructors are great for any properties that need to be created on initialization of an object - but it's also okay to add properties to an object later.

Now we can test our new findContact() method by providing it this Contact's' id and confirming it returns the correct info:

addressBook.findContact(2);

It will return the following:

Contact {firstName: "Grace", lastName: "Hopper", phoneNumber: "503-555-0199", id: 2}

Now findContact() can successfully locate a Contact using the corresponding id.

By the way, another detail that's important to note - and that's not really ideal about JavaScript objects. Remember how when we identified a contact by their first name, we had to do this.contacts["Ada"], not this.contacts[Ada]? We don't have to do that here. You might think that makes sense because the id property is a number, but see what happens when we try this out in the console:

> addressBook.findContact("2");
Contact {firstName: "Grace", lastName: "Hopper", phoneNumber: "503-555-0199", id: 2}

You might think this is because JavaScript is doing loose equality and converting the string into an integer, but actually it's the other way around. Object properties can be strings or symbols but not integers. We can verify that the keys are actually strings by doing the following in the console:

Object.keys(addressBook.contacts)[0];
"1"
typeof Object.keys(addressBook.contacts)[0];
"string"

The Object.keys() method returns all the keys (also known as properties) in an object. We are using bracket notation to look at the first key in addressBook.contacts. You'll see it's "1" - a string! We can even verify this by using typeof to return the type of the key - which shows that it's indeed a string.

Not really ideal - but because JavaScript can do loose equality for us, we can do addressBook.findContact(2) instead of addressBook.findContact("2"). The latter just feels a bit messier - but the messiness is actually coming from how JavaScript objects work. In the next section, we'll learn about a type of object called a Map that cleans up these issues - but for this section, we are going to stay focused on basic objects - in all their glory and messiness.

Deleting Contacts

Let's add one more method for practice. What if we wanted to delete a Contact from our AddressBook? Here's a method to do that. We'll add it right below addressBook.prototype.findContact():

scripts.js
...

AddressBook.prototype.deleteContact = function(id) {
  if (this.contacts[id] === undefined) {
    return false;
  }
  delete this.contacts[id];
  return true;
};

...

It's very similar to AddressBook.prototype.findContact(). If there's no Contact object at the specified id, (this.contacts[id] === undefined) the method will return false because no such contact exists and nothing was deleted.

Otherwise, we'll use delete to delete the contact based on its id property:

delete this.contacts[id];

This will simply remove the key-value pair from the object. We can test it out by adding this method to our console code and then doing the following:

let addressBook = new AddressBook();
let contact = new Contact("Ada", "Lovelace", "503-555-0100");
let contact2 = new Contact("Grace", "Hopper", "503-555-0199");
addressBook.addContact(contact);
addressBook.addContact(contact2);
addressBook.deleteContact(1);
addressBook.contacts;

We'll see that that addressBook.contacts now has only one contact. The currentId is still 2, though, and 1 will never be used as an ID again even though the contact with that ID has been deleted. Once again, that's just how a real database works.

After following along with this lesson, our entire scripts.js file will look like this:

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = {};
  this.currentId = 0;
}

AddressBook.prototype.addContact = function(contact) {
  contact.id = this.assignId();
  this.contacts[contact.id] = contact;
};

AddressBook.prototype.assignId = function() {
  this.currentId += 1;
  return this.currentId;
};

AddressBook.prototype.findContact = function(id) {
  if (this.contacts[id] != undefined) {
    return this.contacts[id];
  }
  return false;
};

AddressBook.prototype.deleteContact = function(id) {
  if (this.contacts[id] === undefined) {
    return false;
  }
  delete this.contacts[id];
  return true;
};

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.phoneNumber = phoneNumber;
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
};

You can also experiment with adding other methods as well. For instance, try adding an Contact.prototype.update() method on the Contact prototype. The main focus in this section is constructors and prototype methods so we encourage you to focus on business logic and increasing your understanding of these key concepts. When working with business logic, make sure to test it in the console before incorporating it into your UI logic.


Example GitHub Repo for the Address Book

Examples


Find a Contact:

AddressBook.prototype.findContact = function(id) {
  if (this.contacts[id] != undefined) {
    return this.contacts[id];
  }
  return false;
};

Delete a Contact:

AddressBook.prototype.deleteContact = function(id) {
  if (this.contacts[id] === undefined) {
    return false;
  }
  delete this.contacts[id];
  return true;
};

Example GitHub Repo for the Address Book

Lesson 10 of 23
Last updated October 21, 2021