I am learning JavaScript based on Eloquent Javascript and during one of the chapters, came across this error. Not sure what I am doing wrong here. I am getting an error "Cannot read property 'indexOf' of undefined" against the code
return journal.events.indexOf(event) != -1
Also, can someone explain how that line works? Isn't IndexOf supposed to return the first position of occurrence of the specified value (in this case, event)? But I see in the book that the line
return journal.events.indexOf(event) != -1;
returns either true or false.var journal = []; function addEntry(events, didITurnIntoASquirrel) { journal.push({ events: events, squirrel: didITurnIntoASquirrel }); } addEntry(["work", "touched tree", "pizza", "running", "television"], false); addEntry(["work", "ice cream", "cauliflower", "lasagna", "touched tree", "brushed teeth"], false); addEntry(["weekend", "cycling", "break", "peanuts", "beer"], true); function hasEvent(event, entry) { return entry.events.indexOf(event) != -1; } console.log(hasEvent("pizza", journal));

- 5
- 3
-
1You've pushed an object *containing* `events` but there's no `events` property on `journal`. – Dave Newton Jun 25 '15 at 17:16
3 Answers
In your sample code journal
is an array
var journal = []; <--- Array
Therefore events
should be accessed with an index like:
journal[0].events.indexOf(event)
^
|
|
Here you need to find the right index to get your events

- 19,834
- 7
- 55
- 102
I'm unsure how far along you are with learning javascript, so forgive me if some of this sounds condescending or obvious.
Let's break this down one step at a time. You begin with an empty array.
var journal = [];
console.log(journal); //[]
//it's defined. It's an empty Array.
By calling push on an array, you add something to the end of the array. More on Array.push.
I don't like this example for beginners because it expects you to know already that you can define an object while you're passing it as an argument. This is done this way because you don't truly need a variable reference to an object that's only used once and is therefore a good way of reducing bloat in code. But verbosity is much better when teaching someone, imho.
//Wait, what am I pushing into the journal array?
journal.push({
events: events,
squirrel: didITurnIntoASquirrel
});
This should make more sense: Create an object first. Then add that object to the "journal" array. function addEntry(events, didITurnIntoASquirrel) { var temporaryObject = { events: events, squirrel: didITurnIntoASquirrel }; journal.push(temporaryObject); }
Now journal is an array with an unnamed object at its first index.
1. console.log(journal); // [temporaryObject]
2. console.log(journal[0]); - //temporaryObject
The visibile difference is the lack of parens, but the difference is important. On line 1 you have the array itself, on line 2 you have what's inside it (i.e. the object). You need to get the object (via the technique on line 2) before you can access properties of that object, such as "events" or "squirrel". Moving on.
addEntry(["work", "touched tree", "pizza", "running", "television"], false);
Next, we invoke the addEntry function. Same confusion here. I've rewritten it slightly to make the arguments more understandable.
var entry = ["work", "touched tree", "pizza", "running", "television"];
addEntry(entry, false);
//repeat 2 more times with different data
So first we define an array, then we pass it to the addEntry function. when the addEntry function runs (it will run right when we invoke it), the "entry" argument will be represented function as the "events" parameter (simple way: events = entry and didITurnIntoASquirrel = false). some notes on parameters vs arguments.
So you should be able to understand now that you're passing an array and a boolean to the addEntry function. That function creates an object based on those values referencing them via their parameters. That object is then added to the journal array.
What you end up with is 4 levels of depth. You have an array called journal, which has objects in it. Those objects have a property called events, which is a different array. That array has several strings inside it. To access the events array and use indexOf to see if it has a given string in it, you need to traverse that depth one level at a time.
//journal is the array, journal[0] is the object, journal[0].events is the property of that object
console.log(journal[0].events) //["work", "touched tree", "pizza", "running", "television"].
Note this is the same data that we originally put in the entry variable. This may seem unnecessarily complicated, but trust me when I say this type of structure is useful in real life when you need to manage data hierarchy or other logical relationships between "things" in Object Oriented programming.
Now, all the work we've done so far is to add to the journal array. We now want a function to see what's inside it. Why a function? So you don't have to rewrite the same code over and over.
function hasEvent(event, journal) {
return journal.events.indexOf(event) != -1;
}
By now I hope you can spot the error in this function. journal.events doesn't work, because journal is an array, not an object (you skipped a level, and your computer isn't smart enough to know what you mean) journal[0].events would work, because you are telling javascript ("from the journal array, I want the object in the first slot, and the events property of that object").
The simplest fix is to send journal[0] to the hasEvent function instead of journal. Beware, this will only check journals first index. Realistically you'd want a for loop inside the hasEvent function or wrapping the call to that function to check all indexes. For now we will hardcode them, since we know there are 3, but its not a good idea in real life, since later there may be more than 3 entries in the journal).
This funciton is returning the result of calling indexOf() (some number or -1) with -1. Let's again rewrite it so that it makes more sense.
New hasEvent function:
//I renamed the variable so it makes more sense what it really is. It's the object, not the journal array.
function hasEvent(event, journalEntry) {
var index = journalEntry.events.indexOf(event);
var result = (index != -1); //true if it was found, false if it wasn't found.
return result; //a boolean based on the above comparison.
}
//Ack! My kingdom for a "for loop". Don't worry about that right now.
console.log(hasEvent("pizza", journal[0]));
console.log(hasEvent("pizza", journal[1]));
console.log(hasEvent("pizza", journal[2]));
TL;DR Here is a fiddle with working code: http://jsfiddle.net/o8dg1ts6/1/

- 1
- 1

- 530
- 4
- 17
-
1How did no one upvote this until I came along? That was an awesome explanation, man. The way the code is written in his example wasn't very explanatory and it seems like it's missing a few steps. This lesson isn't my favorite, but if you were writing it I think I'd like it a hell of a lot more! Thanks for the info, too bad the OP didn't see the value in it. – Trapp Nov 17 '15 at 04:38
To answer your 2nd question:
"Isn't IndexOf supposed to return the first position of occurrence of the specified value"
Yes, and indexOf
returns -1 if if the value is not found in the array.
So if the event is found, then the expression indexOf(event) != -1
will evaluate to true
.

- 1,046
- 7
- 9