Sample Video Frame

19: Data Objects

JavaScript contains the interesting design decision to confuse the word "object" with two different uses. In one way an "object" is a container for data that is key=value style. You might find this form of data in a database table, a spreadsheet, or even if you look at your Contacts on your phone. The other use for "object" is in "object-oriented programming (OOP)", which is a much more complicated topic that we'll cover later in the book. In OOP you are using a combination of objects, classes, and functions to constrain your programming style so that your code can work better with other people's code.

In this exercise we'll focus on objects as a data type (or container), and I'll call them "data objects" to differentiate them from "objects" in object-oriented programming. There really is no difference between a "data object" and an "OOP object". They are the exact same thing but used in different ways. Using an object to store key=value data is the first simplest usage of an object. Then when you learn OOP you'll learn how to add to the "data object" concept for object-oriented programming.

Key/Value Structures

You use key=value data all the time without realizing it. When you read an email you might have:

From: j.smith@example.com
To: zed.shaw@example.com
Subject: I HAVE AN AMAZING INVESTMENT FOR YOU!!!

On the left are the keys (From, To, Subject) which are mapped to the contents on the right of the :. Programmers say the key is "mapped" to the value, but they could also say "set to". As in, "I set From to j.smith@example.com." In JavaScript I might write this same email using a data object like this:

let email = {
    From: "j.smith@example.com",
    To: "zed.shaw@example.com",
    Subject: "I HAVE AN AMAZING INVESTMENT FOR YOU!!!"
};

You create a data object by:

  1. Opening it with a { (curly-brace).
  2. Writing the key, which can be a JavaScript identifier or a string.
  3. Writing a : (colon).
  4. Writing the value, which can be anything that's valid in JavaScript.

Once you do that, you can access this JavaScript email like this:

> email.From
'j.smith@example.com'
> email.To
'zed.shaw@example.com'
> email.Subject
'I HAVE AN AMAZING INVESTMENT FOR YOU!!!'

You'll notice that this is very similar to how you access variables and functions in a module that you require. Using the . (dot) is a primary way you can access parts of many data structures in JavaScript. You can also access this data using the [] syntax from the last exercise:

> email['To']
'zed.shaw@example.com'
> email['From']
'j.smith@example.com'

The only difference from Array indexes is that you use a string ('From') instead of an integer. However! You could use an integer as a key if you want (more on that soon). The reason for this syntax is that you might have keys which are more complicated than just simple single words:

> let names = {'Zed A. Shaw': 44};
undefined
> names['Zed A. Shaw']
44
> names.Zed A. Shaw
names.Zed A. Shaw
          ^

SyntaxError: Unexpected identifier
>

In this case, I used my full name as the key to the names data object, which means that I have to use names['Zed A. Shaw'] to access the value (44). If I don't use that syntax (with the string 'Zed A. Shaw' as the key) then I get an error. Why do I get an error? Because JavaScript will try to process the characters Zed A. Shaw as if it were JavaScript code, and that doesn't make any sense in JavaScript. To make it work I need to use a string for the key, and to do that I have to use the names['Zed A. Shaw'] syntax.

Data Object Gotcha #1

Due to a quirk of syntax you'll run into a slight problem if you try to use a variable or JavaScript expression as the key. Imagine you have this simple code:

> {h: '1'}
{ h: '1' }
> let h = 'hello';
undefined
> {h: '1'}
{ h: '1' }

In this little session I first create a simple object with the named key h, which works as expected. What if I want to use a variable named h instead? I next create the variable with let h = 'hello' which I want to use to set the key to 'hello', not 'h' as I did before. I do what you would expect would work with {h: '1'} but instead of {hello: '1'} I get {h: '1'} like I did above. It's as if JavaScript just ignores me and thinks I want a data object with h as the key, not hello.

The reason this happens is JavaScript can't tell the difference between "use the key h" and "use the value of the variable named h", so it just sides with using that as the key name. In ES6 you can solve this problem by simply wrapping the key you want in square brackets like this:

> {[h]: '1'}
{ hello: '1' }
>

Now it does what we want, and also allows us to use arbitrary JavaScript expressions as a key too:

> {['howdy' + 2]: 'It works!'}
{ howdy2: 'It works!' }

The simplified rule is, if it's a JavaScript identifier (like mykey) or a plain string then write it as-is. If it's anything more complex then put brackets around it.

Combining Lists with Data Objects

A common theme in programming is combining components for surprising results. Sometimes the surprise is a crash or a bug. Other times the surprise is a novel new way to accomplish some task. Either way, what happens when you make novel combinations isn't really a surprise or a secret. To you it may be surprising, but there is usually an explanation somewhere in the language specification (even if that reason is absolutely stupid). There is no magic in your computer, just complexity you don't understand.

A good example of combining JavaScript components is putting data Objects inside Arrays. You can do this:

let messages = [
  {to: 'Sun', from: 'Moon', message: 'Hi!'},
  {to: 'Moon', from: 'Sun', message: 'What do you want Sun?'},
  {to: 'Sun', from: 'Moon', message: "I'm awake!"},
  {to: 'Moon', from: 'Sun', message: 'I can see that Sun.'}
];

Once I do that I can now use Array syntax to access the data objects like this:

> messages[0].to
'Sun'
> messages[0].from
'Moon'
> messages[0].message
'Hi!'
> messages[1]['to']
'Moon'
> messages[1]['from']
'Sun'
> messages[1]['message']
'What do you want Sun?'
>

Notice how I can also use the . (dot) syntax on the data object right after doing messages[0]? Again, you can try combining features to see if they work, and if they do, go find out why because there's always a reason (even if it's stupid).

The for-in Loop

If you remember in Exercise 17 I said that there's one more loop for going through elements of a data object. You use it like this:

let message = {to: 'Sun', from: 'Moon', message: 'Hi!'};
for(let key in message) {
  console.log(`${key}=${message[key]}`);
}

The Code

You are now going to repeat the exercise you did with Arrays and write out three data objects I've crafted. Then you'll require them into node and attempt to access the data I give you. Remember to try to do this in your head and then check your work with node. You should also practice doing this to Array and object structures until you're confident you can access the contents. You'll realize that the data is the same, it's simply been restructured.

exports.fruit = [
    {kind: 'Apples',  count: 12, rating: 'AAA'}, 
    {kind: 'Oranges', count: 1,  rating: 'B'}, 
    {kind: 'Pears',   count: 2,  rating: 'A'},
    {kind: 'Grapes',  count: 14, rating: 'UR'}
];

exports.cars = [
    {type: 'Cadillac', color: 'Black', size: 'Big', miles: 34500},
    {type: 'Corvette', color: 'Red', size: 'Little', miles: 1000000},
    {type: 'Ford', color: 'Blue', size: 'Medium', miles: 1234},
    {type: 'BMW', color: 'White', size: 'Baby', miles: 7890}
];

exports.languages = [
    {name: 'Python', speed: 'Slow', opinion: ['Terrible', 'Mush']},
    {name: 'JavaScript', speed: 'Moderate', opinion: ['Alright', 'Bizarre']},
    {name: 'Perl6', speed: 'Moderate', opinion: ['Fun', 'Weird']},
    {name: 'C', speed: 'Fast', opinion: ['Annoying', 'Dangerous']},
    {name: 'Forth', speed: 'Fast', opinion: ['Fun', 'Difficult']},
];

What You Should See

If you write the code correctly you should be able to do this in node:

> let data = require('./ex19');
undefined
> data.cars[0]
{ type: 'Cadillac', color: 'Black', size: 'Big', miles: 34500 }
>

Keep in mind that you're doing some complicated data access moves here so take it slow. You have to go through the data variable you assign the module to, and then access Arrays, followed by data objects, and in some cases another Array.

The Challenge

I will give you the exact same set of data elements for you to get. Your job is to figure out what indexing you need to get that information. For example, if I tell you fruit 'AAA', then your answer is fruit[0].rating. You should attempt to do this in your head by looking at the code, then test your guess in the node shell.

fuit challenge

You need to get all of these elements out of the fruit variable:

  • 12
  • 'AAA'
  • 2
  • 'Oranges'
  • 'Grapes'
  • 14
  • 'Apples'

cars challenge

You need to get all of these elements out of the cars variable:

  • 'Big'
  • 'Red'
  • 1234
  • 'White'
  • 7890
  • 'Black'
  • 34500
  • 'Blue'

languages challenge

You need to get all of these elements out of the languages variable:

  • 'Slow'
  • 'Alright'
  • 'Dangerous'
  • 'Fast'
  • 'Difficult'
  • 'Fun'
  • 'Annoying'
  • 'Weird'
  • 'Moderate'

Final Challenge

Your final challenge is to write out the JavaScript code that writes out the same song lyric from Exercise 18. Again, take it slow and try to do it in your head before seeing whether you get it right. If you get it wrong, take the time to understand why you got it wrong. For comparison, I wrote out the lyrics in my head in one shot and didn't get it wrong. I am also way more experienced than you are, so you will probably make some mistakes and that is alright.


Learn JavaScript Today

Register today for the Early Access course and get the all currently available videos and lessons, plus all future modules for no extra charge.


Still Not Sure? Try the next FREE Lesson!