While I am also going thru my iOS training, decided to shore up my JS. If you haven't come across this book, I highly recommend it:
Eloquent Javascript.
Anyway, I am in chapter 4 working thru a somewhat challenging problem. It comes down to this: A dude turns in to a squirrel, somewhat randomly. He decided to write down when he becomes one and what events led to it (see data
here).
Just for curiosity's sake, we want to see if there is any correlation between his eating pizza and turning in to a squirrel. To determine this we can calculate a
phi coefficient; an array the following sets of information is what we need: <didn't become a squirrel & no pizza>, <no squirrel & had pizza>, <became a squirrel & no pizza>, <became a squirrel and had pizza>.
This is an interesting problem not only because it requires a certain level of JS knowledge, but also because there are a few ways to solve this problem.
It should be noted that the problem already has a solution in the book, but the interesting part for me is HOW one goes about solving this issue.
I also want to give a shout out to fellow Apple folk who spent some time and we got a working solution: Kenny Crosby & Sonia Brahmi. It was a lot of fun working thru this problem with them and it was a great learning experience for me.
Allright lets start with what what we KNOW we must do- loop thru the "JOURNAL" array (this is just a sample of it, above there is a link for the entire data set):
1
2
3
4
5
6
7
8
9
| var JOURNAL = [
{"events":["carrot","exercise","weekend"],"squirrel":false},
{"events":["bread","pudding","brushed teeth","weekend","touched tree"],"squirrel":false},
{"events":["carrot","nachos","brushed teeth","cycling","weekend"],"squirrel":false},
{"events":["brussel sprouts","ice cream","brushed teeth","computer","weekend"],"squirrel":false},
{"events":["potatoes","candy","brushed teeth","exercise","weekend","dentist"],"squirrel":false},
{"events":["brussel sprouts","pudding","brushed teeth","running","weekend"],"squirrel":false},
{"events":["pizza","brushed teeth","computer","work","touched tree"],"squirrel":false},
{"events":["bread","beer","brushed teeth","cycling","work"],"squirrel":false},
|
How to loop?
1
2
3
4
5
6
7
| function tableFor(journal) {
for (var i = 0; i < journal.length; i++){
}
}
tableFor(JOURNAL);
|
We're clearly not doing anything here, simply calling the "JOURNAL" array. Perhaps a test to see if this works is a good idea, lets call the number of times he turns into a squirrel over the past 90 days
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| function tableFor(journal) {
var squirrelYes = 0;
for (var i = 0; i < journal.length; i++){
if (journal[i].squirrel == true){
squirrelYes += 1
}
}
console.log("turned into a squirrel this many times: " + squirrelYes);
}
tableFor(JOURNAL);
//returns in the console: turned into a squirrel this many times: 5
|
Ok, now we have something to work with. Next up will be to determine what we need to calculate the phi coefficient: didn't become a squirrel & no pizza, no squirrel & had pizza, became a squirrel & no pizza, became a squirrel and had pizza.
Some steps: need to create an array in which we can hold our new found information, and then determine what goes where.
The tricky part is traversing thru each of the various "events" objects within the JOURNAL array to determine if "pizza" exists or not. For example in JOURNAL[0] there is no pizza, but in JOURNAL[6] there is pizza. How do we do that in code?
Fortunately for us all there is a handy guide that the good folks at
Mozilla put together for us. Searching for
"array" shows us a whole lotta methods we can use, specifically, lets look at the
.indexOf() method. This is kinda cool because this method returns the first index point at which a given element can be found in the array or it returns -1 if it is NOT found.
For example:
1
2
3
4
5
6
| var array = ["two", "three", "four"];
array.indexOf("four");
// 2
array.indexOf("five");
// -1
|
Hmmm... this is good, but we have an array of objects! We can still use the .indexOf() method! Yay :)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| var array = [
{"events":["carrot","exercise","weekend"],"squirrel":false},
{"events":["bread","pudding","brushed teeth","weekend","touched tree"],"squirrel":false},
{"events":["carrot","nachos","brushed teeth","cycling","weekend"],"squirrel":false},
{"events":["brussel sprouts","ice cream","brushed teeth","computer","weekend"],"squirrel":false}];
array[0].events.indexOf("carrot");
// 0 carrot was found in the 1st object of the array at the 0 index
array[0].events.indexOf("exercise");
// 1 exercise was found in the 1st object of the array at the 1st index
array[0].events.indexOf("pizza");
// -1 pizza was NOT found in the 1st object of the array
array[3].events.indexOf("ice cream");
// 1 ice cream was found in the 4th object of the array at the 2nd index
array[3].events.indexOf("computer");
// 3 computer was found in the 4th object of the array at the 3rd index
array[3].events.indexOf("picked nose");
// -1 picked nose was NOT found in the 4th object of the array
|
Now that we can find a given element we have to be creative as to how to move thru the JOURNAL.
Taking a look at our existing loop (inside the tableFor function) we are indeed traversing thru the array, and with each loop we are incrementing the variable "i" (i = 0, 1, 2, 3... up to the JOURNAL's length). Since "i" is free, lets use it again when using .indexOf().
Using pseudo-code, we need to go to the "i"
object inside JOURNAL, look at the "events"
property and see if the
value "pizza" exists. Here it is in code:
1
| journal[i].events.indexOf("pizza") !== -1
|
Here, if at the "i" object if pizza exists we will NOT return "-1" and thus it will be true.
This is all getting close to completion! To count up our results we need to setup a variable for that as well, and lets call it table.
Putting it all together we get:
1
2
3
4
5
6
7
8
9
10
11
12
| function tableFor(journal) {
var table = [0];
for (var i = 0; i < journal.length; i++){
if(journal[i].events.indexOf("pizza") !== -1)
table[0] += 1;
}
console.log(table)
}
tableFor(JOURNAL);
// [10] is returned, the total number of times pizza appears in the events property
|
Lets go back to the beginning and revisit what it is we are trying to find: didn't become a squirrel & no pizza, no squirrel & had pizza, became a squirrel & no pizza, became a squirrel and had pizza.
Putting that in to code we get:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| function tableFor(journal) {
var table = [0, 0, 0, 0];
for (var i = 0; i < journal.length; i++){
if(journal[i].squirrel == false && journal[i].events.indexOf("pizza") == -1) {
table[0] += 1;
}
if(journal[i].squirrel == false && journal[i].events.indexOf("pizza") !== -1) {
table[1] += 1;
}
if(journal[i].squirrel == true && journal[i].events.indexOf("pizza") == -1) {
table[2] += 1;
}
if(journal[i].squirrel == true && journal[i].events.indexOf("pizza") !== -1) {
table[3] += 1;
}
}
console.log(table)
}
tableFor(JOURNAL);
// [76, 9, 4, 1]
|
TA DA! We got the solution we've been looking for! But you know what, this isn't "dry" (don't repeat yourself).
Three things immediately stand out: journal[i], the indexOf() method... but perhaps most importantly is how we are calling "pizza". For the journal[i] we can create a variable to hold that in, but the indexOf() method is probably best taken out in to its own function. Lastly, lets move out "pizza" to be another argument when tableFor is called.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| function hasEvent(functionEvent, entry) {
return entry.events.indexOf(functionEvent) !== -1
}
function tableFor(event, journal) {
var table = [0, 0, 0, 0];
for (var i = 0; i < journal.length; i++){
var entry = journal[i];
if(entry.squirrel == false && !hasEvent(event, entry)) {
table[0] += 1;
}
if(entry.squirrel == false && hasEvent(event, entry)) {
table[1] += 1;
}
if(entry.squirrel == true && !hasEvent(event, entry)) {
table[2] += 1;
}
if(entry.squirrel == true && hasEvent(event, entry)) {
table[3] += 1;
}
}
console.log(table)
}
tableFor("pizza", JOURNAL);
// [76, 9, 4, 1]
|
There we go, a slightly different answer than what the book has, but, to be honest, it is more readable and immediately understandable.
My next blog will detail how we go about solving for the
phi coefficient itself and, perhaps, determine what it is that makes this dude turn in to a squirrel.