Skip to content

Latest commit

 

History

History
3456 lines (2743 loc) · 125 KB

README_RAW.md

File metadata and controls

3456 lines (2743 loc) · 125 KB

Prep-Work

0.) Links:

Name Hyperlink
OKTA https://lambdaschoolsso.okta.com/login/login.htm?fromURI=%2Fapp%2FUserHome
Calendar https://calendar.google.com/calendar/u/0/[email protected]
Slack https://app.slack.com/client/T4JUEB3ME/G01QG65CN75
Prep Work https://apply.lambdaschool.com/courses/web/

1.) Platforms we need while attending class:

2.) Web Development Software:

3.) Pre-Course Work Edit epic-hawking-rdij1

.
├── README.html
├── README.md
├── SUMMARY.html
├── SUMMARY.md
├── codeswing.json
├── comparison.png
├── demo.html
├── directory.html
├── index.html
├── javascript-practice
│   ├── 00_expressions_variables
│   │   ├── exercises
│   │   │   ├── A_executing_code_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── B_expressions_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── C_variables_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── D_string_indexing_and_methods_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   
│   │   ├──
│   │   └── solutions
│   │       ├── A_executing_code_solution
│   │       │   ├── age.js
│   │       │   ├── greeting.js
│   │       │   ├──
│   │       │   ├── thirsty.js
│   │       │   └── whoami.js
│   │       ├── B_expressions_solution
│   │       │   ├── boolean-expressions.js
│   │       │   ├── comparisons.js
│   │       │   ├── number-expressions.js
│   │       │   ├──
│   │       │   └── string-expressions.js
│   │       ├── C_variables_solution
│   │       │   ├── four.js
│   │       │   ├── one.js
│   │       │   ├──
│   │       │   ├── three.js
│   │       │   ├── two.js
│   │       │   └── zero.js
│   │       ├── D_string_indexing_and_methods_solution
│   │       │   ├── a-tedious-task.js
│   │       │   ├── dare-to-decipher.js
│   │       │   ├── proper-patterns.js
│   │       │   ├──
│   │       │   └── small-string-snippets.js
│   │       
│   ├── 01_conditionals
│   │   ├── exercises
│   │   │   ├── A_conditionals_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   
│   │   ├──
│   │   └── solutions
│   │       ├── A_conditionals_solution
│   │       │   ├── one.js
│   │       │   ├──
│   │       │   ├── three.js
│   │       │   ├── two.js
│   │       │   └── zero.js
│   │       
│   ├── 02_functions
│   │   ├── exercises
│   │   │   ├── A_functions_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── B_functions_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── C_functions_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   
│   │   ├──
│   │   └── solutions
│   │       ├── A_functions_solution
│   │       │   ├── average.js
│   │       │   ├── ends-with-t.js
│   │       │   ├── half.js
│   │       │   ├── is-div-by-4.js
│   │       │   ├── is-long.js
│   │       │   ├── keep-it-quiet.js
│   │       │   ├──
│   │       │   ├── snippet-0-1.js
│   │       │   ├── snippet-0-2.js
│   │       │   ├── snippet-0-3.js
│   │       │   └── snippet-0-4.js
│   │       ├── B_functions_solution
│   │       │   ├── ends-in-ly.js
│   │       │   ├── funny-sound.js
│   │       │   ├── longer.js
│   │       │   ├── one-or-none.js
│   │       │   ├── parity.js
│   │       │   ├──
│   │       │   ├── starts-with-r.js
│   │       │   ├── string-size.js
│   │       │   └── wacky-word.js
│   │       ├── C_functions_solution
│   │       │   ├── average-of-four.js
│   │       │   ├── case-changer.js
│   │       │   ├── contains.js
│   │       │   ├── divisible.js
│   │       │   ├── in-range.js
│   │       │   ├── larger.js
│   │       │   ├── number-change.js
│   │       │   
│   │       
│   ├── 03_loops
│   │   ├── exercises
│   │   │   ├── A_loops_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── B_loops_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── C_loops_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   
│   │   ├──
│   │   └── solutions
│   │       ├── A_loops_solution
│   │       │   ├── count-up.js
│   │       │   ├── evens.js
│   │       │   ├── min-to-max.js
│   │       │   ├── one-to-four.js
│   │       │   ├──
│   │       │   ├── snippet-1.js
│   │       │   ├── snippet-2.js
│   │       │   ├── snippet-3.js
│   │       │   ├── snippet-4.js
│   │       │   ├── snippet-5.js
│   │       │   └── string-iterate.js
│   │       ├── B_loops_solution
│   │       │   ├── div-by-either.js
│   │       │   ├── five-multiples-of.js
│   │       │   ├── no-ohs.js
│   │       │   ├── odd-sum.js
│   │       │   ├── product-up-to.js
│   │       │   ├──
│   │       │   ├── string-repeater.js
│   │       │   └── sum-up-to.js
│   │       ├── C_loops_solution
│   │       │   ├── censor-e.js
│   │       │   ├── divisible-range.js
│   │       │   ├── fizz-buzz.js
│   │       │   ├── raise-power.js
│   │       │   ├── remove-capitals.js
│   │       │   ├── reverse-iterate.js
│   │       │   
│   │       
│   ├── 04_arrays
│   │   ├── exercises
│   │   │   ├── A_arrays_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── B_arrays_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── C_arrays_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   
│   │   ├──
│   │   └── solutions
│   │       ├── A_arrays_solution
│   │       │   ├── bleep-vowels.js
│   │       │   ├── divisors.js
│   │       │   ├── filter-long-words.js
│   │       │   ├── num-odds.js
│   │       │   ├──
│   │       │   ├── stay-positive.js
│   │       │   ├── strings-to-lengths.js
│   │       │   └── total.js
│   │       ├── B_arrays_solution
│   │       │   ├── choose-divisibles.js
│   │       │   ├── make-acronym.js
│   │       │   ├── maximum.js
│   │       │   ├── reverse-array.js
│   │       │   ├──
│   │       │   ├── word-count.js
│   │       │   └── your-average-function.js
│   │       ├── C_arrays_solution
│   │       │   ├── alternating-caps.js
│   │       │   ├── common-elements.js
│   │       │   ├── lengthiest-word.js
│   │       │   ├── number-range.js
│   │       │   ├── remove-short-words.js
│   │       │   
│   │       
│   ├── 05_nesting
│   │   ├── exercises
│   │   │   ├── A_nested_loops_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── B_two_dimensional_arrays_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   ├── C_more_problems_exercise
│   │   │   │   ├── README.html
│   │   │   │   ├── README.md
│   │   │   │   
│   │   │   
│   │   ├──
│   │   └── solutions
│   │       ├── A_nested_loops_solution
│   │       │   ├── pair-print.js
│   │       │   ├── print-combinations.js
│   │       │   ├──
│   │       │   ├── snippet-1.js
│   │       │   ├── snippet-2.js
│   │       │   ├── snippet-3.js
│   │       │   ├── snippet-4.js
│   │       │   ├── snippet-5.js
│   │       │   └── two-sum.js
│   │       ├── B_two_dimensional_arrays_solution
│   │       │   ├── make-matrix.js
│   │       │   ├── print-2d.js
│   │       │   ├──
│   │       │   ├── total-product.js
│   │       │   ├── two-sum-pairs.js
│   │       │   └── zipper.js
│   │       ├── C_more_problems_solution
│   │       │   ├── remove-dupes.js
│   │       │   ├── remove-first-vowel.js
│   │       │   ├── remove-vowels.js
│   │       │   ├──
│   │       │   ├── shorten-long-words.js
│   │       │   └── spam.js
│   │       
│   ├── 06_decomposition_pattern
│   │   ├── A_decomposing_problems_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   ├── B_decomposing_problems_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   ├── C_decomposing_problems_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   
│   ├── 07_objects
│   │   ├── A_objects_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   ├── B_objects_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   ├── C_objects_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   
│   ├── 08_higher_order_functions
│   │   ├── A_native_callback_methods_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   ├── B_native_callback_methods_exercise
│   │   │   ├── README.html
│   │   │   ├── README.md
│   │   │   
│   │   
│   
├── js.js
├── one-of-every-tag.html
├── readme
│   ├── README.html
│   ├── README_files
│   │   ├── katex.min.css
│   │   
│   
├── resources.html
├── resources.md
├── script.js
├── style.css
└── tree.md

66 directories, 241 files





WEEK 1
Introduction to JavaScript (Part 1) {ignore=true}


Expression Learning Objectives Intro to Functions Learning Objectives

Control Flow and Array Learning Objectives

Intermediate Functions Learning Objectives


WEEK-01 DAY-1
Function Introduction {ignore=true}


Expression Learning Objectives

Below is a complete list of the terminal learning objectives for this lesson. When you complete this lesson, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for this lesson.

  1. Given a working REPL interface, write and execute a statement that will print "hello world" using console.log

  2. Identify that strings are a list of characters defined by using double or single quotes

  3. Given an arithmetic expression using +, -, *, /, %, compute its value

  4. Given an expression, predict if its value is NaN

  5. Construct the truth tables for &&, ||, !

  6. Given an expression consisting of >, >=, ===, <, <=, compute it's value

  7. Apply De Morgan's law to a boolean expression

  8. Given an expression that utilizes operator precedence, compute its value

  9. Given an expression, use the grouping operator to change it's evaluation

  10. Given expressions using == and ===, compute their values

  11. Given a code snippet using postfix ++, postfix --, +=, -=, /=, *=, predict the value of labeled lines

  12. Create and assign a variable using let to a string, integer, and a boolean. Read its value and print to the console.l learning objectives for this lesson. When you complete this lesson, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for this lesson.

  13. Given a working REPL interface, write and execute a statement that will print "hello world" using console.log

  14. Identify that strings are a list of characters defined by using double or single quotes

  15. Given an arithmetic expression using +, -, *, /, %, compute its value

  16. Given an expression, predict if its value is NaN

  17. Construct the truth tables for &&, ||, !

  18. Given an expression consisting of >, >=, ===, <, <=, compute it's value

  19. Apply De Morgan's law to a boolean expression

  20. Given an expression that utilizes operator precedence, compute its value

  21. Given an expression, use the grouping operator to change it's evaluation

  22. Given expressions using == and ===, compute their values

  23. Given a code snippet using postfix ++, postfix --, +=, -=, /=, *=, predict the value of labeled lines

  24. Create and assign a variable using let to a string, integer, and a boolean. Read its value and print to the console.


Intro to Functions Learning Objectives

Below is a complete list of the terminal learning objectives for this lesson. When you complete this lesson, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for this lesson.

  1. Define a function using function declaration

  2. Define a function that calculates the average of two numbers, call it, pass in arguments, and print it's return value

  3. Identify the difference between parameters vs argument terminal learning objectives for this lesson. When you complete this lesson, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for this lesson.

  4. Define a function using function declaration

  5. Define a function that calculates the average of two numbers, call it, pass in arguments, and print it's return value

  6. Identify the difference between parameters vs arguments


Hello World

Hey Programmer! Welcome to the JavaScript module. In the next few sections, we'll be learning the fundamentals of the JavaScript programming language. If it's your first time programming, don't worry; we designed this course especially for you. We'll have you executing your first lines of code in no time!

When you finish this article, you should be able to:

  • use the console.log command to print out messages
  • use double slashes (//) to write code comments

Getting visual feedback in your programs

The first command we'll learn in JavaScript is console.log. This command is used to print something onto the screen. As we write our first lines of code, we'll be using console.log frequently as a way to visually see the output of our programs. Let's write our first program:

console.log("hello world");
console.log("how are you?");

Executing the program above would print out the following:

hello world
how are you?

Nothing too ground breaking here, but pay close attention to the exact way we wrote the program. In particular, notice how we lay out the periods, parentheses, and quotation marks. We'll also terminate lines with semicolons (;).

Depending on how you structure your code, sometimes you'll be able to omit semicolons at the end of lines. For now, you'll want to include them just as we do.

Syntax

We refer to the exact arrangement of the symbols, characters, and keywords as syntax. These details matter - your computer will only be able to "understand" proper JavaScript syntax. A programming language is similar to a spoken language. A spoken language like English has grammar rules that we should follow in order to be understood by fellow speakers. In the same way, a programming language like JavaScript has syntax rules that we ought to follow!

As you write your first lines of code in this new language, you may make many syntax errors. Don't get frustrated! This is normal - all new programmers go through this phase. Every time we recognize an error in our code, we have an opportunity to reinforce your understanding of correct syntax. Adopt a growth mindset and learn from your mistakes.

Additionally, one of the best things about programming is that we can get such immediate feedback from our creations. There is no penalty for making a mistake when programming. Write some code, run the code, read the errors, fix the errors, rinse and repeat!

Code comments

Occasionally we'll want to leave comments or notes in our code. Commented lines will be ignored by our computer. This means that we can use comments to write plain english or temporarily avoid execution of some JavaScript lines. The proper syntax for writing a comment is to begin the line with double forward slashes (//):

// let's write another program!!!
console.log("hello world");

// console.log("how are you?");

console.log("goodbye moon");

The program above would only print:

hello world
goodbye moon

Comments are useful when annotating pieces of code to offer an explanation of how the code works. We'll want to strive to write straightforward code that is self-explanatory when possible, but we can also use comments to add additional clarity. The real art of programming is to write code so elegantly that it is easy to follow.

"Simplicity is prerequisite for reliability." — Edsger W. Dijkstra

Remember

  • console.log can be used to print to the screen
  • using // at the front of a line will turn it into a comment; comments are ignored by JavaScript

The Number Type

The Number data type in JavaScript does exactly what you expect! It is used to represent any numerical values, this includes integers and decimal numbers. As one of our first data types, we'll be interested in what operations we can use with numbers.

When you finish this article, you should be able to:

  • predict the evaluation of arithmetic expressions
  • explain the order of operations for JavaScript's arithmetic operators
  • use the grouping operator, (), to manipulate the order of operations in an expression

All the numbers

JavaScript's Number encompasses numerical values. All of the following values are of number type:

42;
-5;
3.14159;
7.0;

The basic arithmetic operators

For any given data type, we're interested in what operations we can perform with that type. We use the word operator to refer to the symbol that performs a particular operation. For example, the + operator performs the addition operation. Here are the common arithmetic operators in JS:

  • + (addition)
  • - (subtraction)
  • * (multiplication)
  • / (division)
  • % (modulo)

With number values and arithmetic operators in hand, we can evaluate our first expressions:

console.log(2 + 3); // => 5
console.log(42 - 42); // => 0
console.log(-4 * 1.5); // => -6
console.log(25 / 8); // => 3.125

Nothing too groundbreaking about the results above. An expression consists of values and operators. JavaScript will evaluate an expression into a single value.

We can write more complex expressions using multiple operators. However, we'll want to be aware of the general math order of operations. That is, we perform multiplication-division operations first and then addition-subtraction operations. To force a specific order of operation, we can use the grouping operator, ( ), around a part of the expression:

console.log(5 * 3 + 2); // => 17
console.log(2 + 3 * 5); // => 17
console.log((2 + 3) * 5); // => 25

The modulo operation

All of the math operators listed above are the simple operations you use everyday, except for maybe modulo %. Modulo gives us the remainder that results from a division. For example, 10 % 3 is 1 because when we divide 10 by 3, we are left with a remainder of 1. We can read 10 % 3 as "ten modulo three" or "ten mod three."

console.log(10 % 3); // => 1
console.log(14 % 5); // => 4
console.log(20 % 17); // => 3
console.log(18 % 6); // => 0
console.log(7 % 9); // => 7

Modulo is a very useful operation in the realm of computers. We can use it to check the divisibility of numbers, whether numbers are even, whether they are prime, and much, much more. Don't take this seemingly simple operation from granted! We'll provide a ton of practice using these modulo patterns as we move through the course.

In the order of operations, modulo has the the same precedence as multiplication-division. So our complete order of math operations in JS is parentheses, multiplication-division-modulo, addition-subtraction.

// modulo has precedence over addition
console.log(4 + 12 % 5); // => 6
console.log((4 + 12) % 5); // => 1

Remember

  • The Number type is used to represent integer and decimal values
  • The operators +, -, /, * perform the normal math operations of addition, subtraction, division, multiplication respectively
  • a % b returns the remainder when we divide a by b; we call this operation modulo
  • JavaScript follows the usual mathematical order of operations and we can use the () to force precedence

The String Type

This article is about one of JavaScript's primitive data types, String. Strings are what we'll use to represent textual data. This means that strings are useful in representing things like messages, names, poems, and so on. A string is a sequence of characters.

When you finish this article, you should be able to:

  • Write strings using correct syntax
  • Use .length to obtain a count of the numbers of characters that comprise a string
  • Index a string to refer to a single character
  • Concatenate strings together

Writing a valid string

Strings are always wrapped by a pair of single quotation marks (') or by a pair of double quotation marks ("). Between the enclosing quotation marks, we can put any characters! Here are a six examples of strings:

"potato";
"New York";
"[email protected]";
"Follow the yellow brick road, please!";
"365 days a year";
"";

Above, notice that we are free to mix in any characters into a string. This includes spaces, numerics, punctuation, and other symbols. The sixth string above is the empty string; it contains zero characters!

You are probably wondering why we are allowed to use either single or double quotes when denoting a string - why is this useful? Maybe we want a string that contains quotation marks:

// valid strings
'Shakespeare wrote, "To be or not to be"';
"That's a great string";
// invalid string
'That's a bad string'

If we want to use a single quote as a character of a string, we simply need to enclose the string in double quotes, and vice versa.

Calculating length

Since a single string can contain any number of characters, we may find it useful to count the number of characters in a string using .length:

console.log("ramen".length); // => 5
console.log("go home!".length); // => 8
console.log("".length); // => 0

Indexing a string

Strings consist of multiple characters. These characters are numbered by indices starting at 0. So in the string 'bootcamp', 'b' is at index 0, 'o' is at index 1, 'o' is at index 2, 't' is at index 3, and so on. We can look at particular characters of a string by using [] and specifying an index:

console.log("bootcamp"[0]); // => 'b'
console.log("bootcamp"[1]); // => 'o'
console.log("bootcamp"[2]); // => 'o'
console.log("bootcamp"[3]); // => 't'
console.log("bootcamp"[7]); // => 'p'
console.log("bootcamp"[10]); // => undefined
console.log("bootcamp"[-3]); // => undefined

In general, when we index a string using the expression string[i], we get back the single character at position i. Looking at the last two examples above, if we use an invalid index with a string, the value returned is undefined. This makes since because there is no character at the given position! It's also worth mentioning that an index should always be a number.

The classic "off by one" error

Bear in mind that indices begin at 0 and not 1! Forgetting this nuance can lead to incorrect code for both new and experienced programmers alike. Let's hone in on an important distinction: the index of the last character of a string is always one less than it's length.

console.log("cat".length); // => 3
console.log("cat"[3]); // => undefined
console.log("cat"[2]); // => 't'

In other words, although the length of 'cat' is 3, the index of the last character ('t') is 2.

Using indexOf

We can also calculate the index of a given character within a string by using indexOf:

console.log("bagel".indexOf("b")); // => 0
console.log("bagel".indexOf("a")); // => 1
console.log("bagel".indexOf("l")); // => 4
console.log("bagel".indexOf("z")); // => -1

If we attempt to search for a character that is not present in a string, indexOf will return -1. This makes sense because we know that -1 is not a valid string index. The smallest index possible is 0!

If we search for a character that appears more than once in a string, indexOf will return the index of the first occurance of that character.

We can also use indexOf to search for a substring of characters. Under this circumstance, indexOf will return the index where the substring begins in the main string:

console.log("door hinge".indexOf("oor")); // => 1
console.log("door hinge".indexOf("hi")); // => 5
console.log("door hinge".indexOf("hint")); // => -1

Concatenation

Concatenation is just a fancy word for joining strings together into a single string. To concatenate strings, we use the + operator:

console.log("hello" + "world"); // => 'helloworld'
console.log("goodbye" + " " + "moon"); // => 'goodbye moon'

Remember

  • a String is a data type that contains multiple characters enclosed in quotation marks
  • string.length returns the number of characters in the string
  • each character of a string is associated with a number index; the first character of a string is at index 0
  • we can use string.indexOf(char) to obtain the index of char within string; if char is not found, then -1 is returned
  • we can use + to concatenate multiple strings, combining them into a single string

The Boolean Type

The Boolean data type is perhaps the simplest type since there are only two possible values, true and false. However, we'll find booleans very useful because they will act as components of later concepts. As programmers, we'll use booleans to describe the validity of statements. In an abstract sense, "Today is Monday" and "one plus one equals ten" are examples of statements with boolean values. That is, they are either true or false.

When you finish this article, you should be able to:

  • predict the evaluation of expressions that use the boolean operations of !, ||, and &&
  • explain DeMorgan's law

Logical Operators

In the long run, we'll be using booleans to establish logic in our code. For this reason, the boolean operators can also be referred to as the logical operators. There are only three such operators:

  • ! (not)
  • && (and)
  • || (or)

Logical NOT

The not (!) operator will reverse a boolean value:

console.log(!true); // => false
console.log(!false); // => true
console.log(!!false); // => false

It's worth mentioning that ! is a unary operator. This means that the not operation is applied to a single value. This is in contrast to a binary operator such as multiplication, which is applied between two values. It does not make sense to ! two values together.

Logical AND

The and (&&) operator will take two boolean values and will only evaluate to true when both input values are true. Otherwise, it will return false:

console.log(false && false); // => false
console.log(false && true); // => false
console.log(true && false); // => false
console.log(true && true); // => true

Logical OR

The or (||) operator will take two boolean values and will only evaluate to false when both input values are false. Otherwise, it will return true:

console.log(false || false); // => false
console.log(false || true); // => true
console.log(true || false); // => true
console.log(true || true); // => true

Logical order of operations

We can write boolean expressions that consist of multiple logical operations, but we should be aware of the order of operations. JavaScript will evaluate ! then && then ||.

console.log(true || true && false);    // => true
console.log(false && !(false || true)); // => false

In general, A || B && C is equivalent to A || (B && C) where A, B, C are booleans.

De Morgan's Law

A common mistake in boolean logic is to incorrectly distribute ! across parentheses. Say we had boolean values of A, B. Here is something to remember:

  • !(A || B) is equivalent to !A && !B
  • !(A && B) is equivalent to !A || !B

In other words, to correctly distribute ! across parentheses, we must also flip the operation within parentheses. Beware that:

  • !(A || B) is not equivalent to !A || !B
  • !(A && B) is not equivalent to !A && !B

We call this property De Morgan's Law. Shout out to Augustus De Morgan of Great Britain.

Remember

  • !, &&, || are the boolean operators that we can use to establish logic in our code
  • De Morgan's Law should be used to distribute ! against parentheses

These are just the basics of the type. We'll be seeing more booleans in the upcoming section, so stay tuned for that!


Comparison Operators

In our previous introduction to the boolean data type, we described booleans as way to represent the validity of an expression. We'll continue this conversation by exploring comparison operators. As you learn about these operators, bear in mind that all comparisons will result in a boolean, true or false.

When you finish this article, you should be able to:

  • Predict the result of expressions that utilize the operators >, <, >= <=, ===, and !==
  • Explain the difference between the equality operators == and ===

The relative comparators

  • > (greater than)
  • < (less than)
  • >= (greater than or equal to)
  • <= (less than or equal to)
  • === (equal to)
  • !== (not equal to)

Using these operators is pretty straightforward. Here are a few examples:

console.log(10 > 5); // => true
console.log(10 < 5); // => false
console.log(1 < 7); // => true
console.log(7 <= 7); // => true
console.log(5 === 6); // => false
console.log(5 !== 6); // => true
console.log("a" !== "A"); // => true
console.log(false === false); // => true

Notice that a comparison expression always evaluate to a boolean value (true or false). Comparison operators like === are a useful to compare strings, booleans, etc. not just numbers.

Did you know? 'a' < 'b' is valid JS code? When you relatively compare strings using > or < you will be comparing them lexicographically. Lexicographically is fancy shmancy talk for "dictionary" order! A "lesser" string is one that would appear earlier in the dictionary:

console.log("a" < "b"); // => true
console.log("apple" < "abacus"); // => false
console.log("app" < "apple"); // => true
console.log("zoo" > "mississippi"); // => true

Gotcha capitilized letters are considered lexicographically less than lower case letters. i.e "A" < "z" // => true.

=== vs ==

In JavaScript there are two equality operators triple-equals (===) and double-equals (==). The operators differ in how they compare across differing types. Triple-equals performs the strict equality, meaning it will only return true if the types are the same. Double-equals performs the loose equality, meaning it can return true even if the values are of different type. Double-equals may coerce a value into another type for the comparison, and this behavior is hard to predict:

console.log(5 === "5"); // false
console.log(5 == "5"); // true
console.log(0 === false); // false
console.log(0 == false); //true

Whoa! Surprised by these results? It can be hard to predict how == behaves, so we will avoid using it in this course and as a best practice. Stick to using === because it respects data types.

Remember

  • >, <, >=,<=, ===, and !== can be used to compare values
  • we prefer to use === to check for equality because it takes the type into account.

Basic Variables

Variables are used to store information to be referenced and manipulated in a computer program. They also provide a way of labeling data with a descriptive name, so our programs can be understood more clearly by programmers. It is helpful to think of variables as containers that hold information. Their sole purpose is to label and store data in computer memory. This data can then be used and even changed throughout the lifetime of your program.

When you finish this reading, you should be able to:

  • declare variables using the let keyword
  • assign values to variables using the assignment operator (=)
  • use the shortcuts +=, -=, ++, -- to reassign variables
  • identify undefined as the default value for unassigned variables

Initializing a variable

To initialize a variable in JavaScript we'll need two new pieces of syntax: let and =. We can give the variable any name that we wish and assign it a value. Once we initialize a variable, the variable will evaluate to the value assigned:

let bootcamp = "";
console.log(bootcamp); // ''

let birthYear = 2012;
console.log(birthYear); // 2012

Did you know? JavaScript variables names can contain any alphanumeric characters, underscore (_), or dollar sign ($). However, they cannot begin with a number.

Above are examples of how you'll create variables most of the time, so we'll grow very familiar with the syntax. As a best practice, we should name our variables in a way that is descriptive and concise.

The variable initializations above really consist of two steps: declaration with let and assignment with =. Let's break these two steps down.

Declaring a variable

In JavaScript, in order to use a variable, we must declare it. Variable declaration is the act of introducing the variable to the environment.

To declare a variable, use the let keyword, followed by a space and then the name of the variable.

let bootcamp;
console.log(bootcamp); // undefined

Once a variable is declared, it will contain undefined as it's value. undefined is a common default value in JavaScript, we'll see it come up in a few different places. You can think of undefined as showing that the variable is empty.

Assigning a variable

Once a variable has been declared, we can assign it a value using single-equals = :

let bootcamp;
console.log(bootcamp); // undefined
bootcamp = "";
console.log(bootcamp); // ''

Manipulating variables

To change the value of a variable, we need to reassign it to a new value with = :

let num = 42;
console.log(num + 8); // => 50
console.log(num); // => 42

num = num + 10;
console.log(num); // => 52

In the code above, num + 8 will evaluate to 50, but it will not change the num variable to 50. If we want to change the num variable, we must reassign to it.

Assignment Shorthand

Changing the value of a number variable is something fairly common in the programming world. Luckily there is some shorthand operators we can use:

let number = 0;
number += 10; // equivalent to number = number + 10
number -= 2; // equivalent to number = number - 2
number /= 4; // equivalent to number = number / 4
number *= 7; // equivalent to number = number * 7
console.log(number); // 14

We also have other shorthand to add or subtract exactly 1 from a variable, the increment (++) and decrement (--) operators:

let year = 3004;
year++;
console.log(year); // 3005
year--;
console.log(year); // 3004

NaN

Now that we have the ability to perform arithmetic with variables, let's take a look at a common programming mistake, getting a result of NaN (not a number):

let num;
console.log(num + 3); // NaN

The above code gives NaN because the unassigned num variable contains undefined; adding 3 to undefined results in NaN. In general, any nonsensical arithmetic will result in NaN. Math operations involving undefined is perhaps the most common mistake:

console.log(undefined + 3); // NaN
console.log("fish" * 2); // NaN

Remember

  • variables are declared with let and will contain the value undefined by default
  • we can use single-equals = to assign variables
  • changing a variable requires a reassignment, for which there are many shortcuts for (+=, -=, etc.)

Introduction to Functions

We hope you are ready - because you are on the brink of one of the most fun parts of writing JavaScript: writing functions. A function is a procedure of code that will run when called. We only "write" a function once (function declaration), but we can "use" it as many times as we please (function calls). Functions are the fundamental building blocks of JavaScript and mastering them is a big step on the road to JavaScript mastery.

When you finish this reading, you should be able to:

  1. Describe what a function in JavaScript is.
  2. Demonstrate how to invoke a function.
  3. Write a function using function declaration.
  4. Use the return keyword to return a value from a function.

Writing Functions

A function is a set procedure of code that will run when called. Functions really start to make sense when put in the perspective of solving problems. So for example say you want to find the average of two given numbers. Meaning we want to take two numbers, add them together then divide by 2:

> (5 + 5) / 2
5

> (15 + 3) / 2
9

> (7 + 2) / 2
4.5

Writing out the same code again and again gets tedious fast. What you can do instead is write a new function.

Function Declaration

A function definition consists of the function keyword, followed by three things:

  1. The name of the function.
  2. A list of parameters to the function, enclosed in parentheses, ().
  3. The code to be run when this function is run, enclosed in curly brackets,{ }.

So for our above example of averaging two numbers we could write a function that would do that for us! We would write something like the following:

// 1. average is the name of the function
// 2. number1 & number2 are the parameters being passed in
function average(number1, number2) {
  // 3. this is the code run every time this function is used
  return (number1 + number2) / 2;
}

First thing to notice for the above average function is that we didn't use any real numbers. You always want to write functions to accept as wide a range of data as possible. Utilizing the incoming parameters to a function is one of the keys to making functions flexible.

In the case of the average function, we want to use it to calculate the average of any two numbers. number1 and number2 are the parameters for the average function. In other words, the average function expects to be given two numbers, number1 and number2. We'll be talking a lot more about parameters later - but for now know that when you define a function with parameters you are declaring those parameters as usable variables within that function.

The beauty of a function is that if we define it in a clever way, it will work on a whole slew of data! For example, we want average to work on any two numbers, whether or not they are whole numbers, decimal, negative, etc.

Invoking or "calling" a function

Now that we've written a function how do we actually use it? Once defined a function can be invoked or "called" as many times as we please.

Order of code

Let's step away from average for a bit to see how a simple function call works. Say we run JavaScript code that looks like this:

console.log("First!");

console.log("Second!");

Running this code will return exactly as we expect. We will see First! printed out, followed by Second!. In other words, JavaScript will evaluate your code left-to-right and top-to-bottom. Very intuitive! It's exactly how you are reading these notes right now.

However, when JavaScript sees a function definition, JavaScript will not evaluate the code inside of the definition. It will only "remember" the code so we can execute it later. The code below only prints First! followed by Fourth!:

console.log("First!");

function callMe() {
  console.log("Second!");
  console.log("Third!");
}

console.log("Fourth");

// when run this code is ran it will print out:
// "First!"
// "Fourth"

To actually get the code within callMe to evaluate, we must call it by using callMe(). The code below will now print out in order:

function callMe() {
  console.log("Second!");
  console.log("Third!");
}

console.log("First!");
// we call the function by adding ending parenthesis
callMe();
console.log("Fourth!");

// when run this code is ran it will print out:
// "First!"
// "Second!"
// "Third!"
// "Fourth"

Let's say JavaScript is running the file above. Here are the steps it would take, starting from the tippy top of the code:

  1. JS sees a definition for callMe. It will remember this definition in case we call the function later. It will not evaluate the code inside the function yet.
  2. JS prints out First!
  3. JS sees that we are calling callMe(). At this point it will look at the prior callMe definition and run the code inside. It is as if we are "jumping" to inside the function definition. This means it will print Second! followed by Third!
  4. JS sees there is no more code to be run inside of callMe, so it "jumps" back to where we originally called callMe()
  5. JS will continue evaluating in order and print Fourth!

An average example

So a declared function is "saved for later use", and will be executed later, when it is called, also known as being invoked. So thinking back to our average function we can declare the function and then invoke it.

When we specify what data to use for a function call, we refer to that process passing arguments to the function.

// this is a function definition
function average(number1, number2) {
  return (number1 + number2) / 2;
}

// this is a function call with the arguments being 15 & 3
> average(15, 3)
9

// this is a function call with the arguments being 5 & 5
> average(10, 5)
7.5

When we call the function average(15, 3), we run the code inside the definition for average. That is, we plug in the parameters with real numbers (number1 becomes 10 and number2 becomes 16). Think of number1 and number2 as variables that contain the values we pass in when we called the function. Then we proceed by running the code inside the function. The parameter names number1 and number2 used through the body of the function and behave like variables.

Returning a value

Now that we know how functions are declared and invoked let's talk about the inside of the function. We'll start with a statement: Every function in JavaScript returns undefined unless otherwise specified.

Now what does that mean? We'll start with a simple example:

function sayNumber(number) {
  console.log(number);
}

> sayNumber(1); // prints 1
1
undefined

So what happened there? Let's do a quick step by step:

  1. We declared the sayNumber function
  2. sayNumber was called handing in the argument of 1
  3. The number parameter is printed to the console
  4. Then the function ends without encountering a return statement. Since nothing was specifically returned then the function returned the default value for a function which is undefined.

Now let's change our above example to use the keyword return to return a value:

function sayNumber(number) {
  console.log(number);
  return true;
}

> sayNumber(1);
1 // sayNumber still prints 1
true // but now sayNumber returns as true

Let's go back to our previous average function and talk about the return we used there:

function average(number1, number2) {
  return (number1 + number2) / 2;
}

// the function call for average(10, 16) will return 13
// so the result variable will be set to 13
let result = average(10, 16);

// if we want to check what a function returns we can do this:
console.log(result); // prints `13`

// we could alternatively do this:
console.log(average(10, 16));

When we call a function, we jump to the function definition and run the code inside. When we hit a return statement, we immediately exit the function, jump back to where we called the function, and evaluate the function call to the value it returned.

Every function call evaluates to it's return value! In other words, the expression average(10, 16) evaluates to 13 just like how the expression 1 + 1 evaluates to 2.

Another important rule of the return statement is that it stops function execution immediately. This means that any code after a return will not be executed!

function average(number1, number2) {
  let sum = number1 + number2;
  return sum;
  // anything under the first return will not be executed
  console.log("this will not run")
  return false;
}

 // when the first return is encountered the entire function will return a value
> average(2, 7);
9

So the three things to remember about return statements is:

  1. Every function call evaluates to it's return value.
  2. Every function in JavaScript returns undefined unless a return is specified
  3. Once a return statement is encountered the function will immediately stop and return the value, ignoring any code below the return statement.

The importance of naming

A quick but very important side note about good naming. Take this to heart right now: Good names are important. Do yourself, and every other programmer reading your code, a favor by always using significant function and variable names.

For example, x is a very non-descriptive name for a variable or function. As we tackle more complicated problems and our code grows to be more complex, we are likely to forget what badly named variables originally stood for and what their purpose was. Non-descriptive names make our code error-prone. Great code reads like English and almost explains itself. As programmers, our goal is to write code that is not only "correct", but also elegant, readable, and maintainable! Hold yourself to this high standard.

As far as syntax goes in JavaScript we always name our functions and variables camelCase for multiple words. (Ex: tipCalculator, currentNumber, puppyPartyFinder). Other languages use other conventions so it's best to pick up the standard for your chosen language and stick with it.

Review

By writing a function we can reuse code over and over again to solve similar problems with different input data (arguments). This will make your life easier and allow you to start working on more complex problems.

This reading covered:

  • How to define and invoke a function in JavaScript.
  • How to use the return keyword to return a value from a function.
  • Writing readable JavaScript code by using significant names and following camelCase conventions for multiple word variables and functions

Parameters and Arguments

When talking about functions one of the first things we mentioned was the word parameters. In this reading we will be covering what exactly a parameter is - as well as the differentiation between parameters and arguments.

When you finish this reading, you should be able to:

  1. Identify the difference between parameters and arguments.
  2. Write a function that utilizes declared parameters.
  3. Invoking a function with passed in arguments.

The difference between Parameters and Arguments

Let's start off by talking about the difference between arguments and parameters and how to identify which is which.

  1. Parameters are comma separated variables specified as part of a function's declaration.
  2. Arguments are values passed to the function when it is invoked.

So by defining parameters when we declare our function we are effectively setting accessible variables within the function:

function add(firstParameter, secondParameter) {
  console.log(firstParameter + secondParameter);
}

// the add function declares two parameters
> add(1, 2); //=> 3

In the above example we declared our parameters when we declared our function. Now arguments work slightly differently - when the function is invoked we are passing in arguments. So in the above example when we invoked add(1, 2) the (1,2) were the arguments being passed in. So when a function is invoked the value of the declared parameters is assigned to the passed in arguments.

You can think of it parameters and arguments like a recipe. A recipe is a list of ingredients (parameters) and list of steps (the code to be run). When someone cooks the recipe (invokes the function) they add the ingredients they actually have(arguments). The result of cooking the recipe is the delicious return value!

Extra arguments

In JavaScript a function will not throw an error if the number of arguments passed during a function invocation is different than the number of parameters listed during function declaration. This is very important to know!

Let's use the above function to demonstrate:

function add(firstParameter, secondParameter) {
  console.log(firstParameter + secondParameter);
}

// this will ignore the 17 & 14
// the first two arguments passed in will be assigned to the first two parameters
> add(1, 2, 17, 14); //=> 3

Notice in the above example we passed in 4 arguments (1, 2, 17, 14) to add. Since the function was only looking for two parameters that is all it uses.

Not enough arguments

Now what happens if we pass in less arguments then needed?

function add(firstParameter, secondParameter) {
  console.log(firstParameter + secondParameter);
}

> add(5); //=> NaN

Whoa what happened there? Let's do a play-by-play:

  1. firstParameter was set to equal the first passed in argument which in the above case is 5.
  2. Since there is no second argument then secondParameter is declared as a variable but is set to the default value of undefined.
  3. The function then tries to add 5 to undefined which is definitely not a number! So we get NaN (which means Not A Number) printed to the console.

As you write more functions you'll grow very comfortable using both arguments and parameters to accomplish your function's goal.

Review

  • Parameters are variables defined as a part of a function's declaration.
  • Arguments are values passed to the function when it is invoked.
  • JavaScript functions can intake a different number of arguments than the number of parameters listed during function declaration.

WEEK-01 DAY-2
Control Flow {ignore=true}


Control Flow and Array Learning Objectives

Below is a complete list of the terminal learning objectives for this lesson. When you complete this lesson, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for this lesson.

  1. Define a function that accepts a sentence string and two words as args. The function should return a boolean indicating if the sentence includes either word.

  2. Identify a pair of mutually exclusive conditions

  3. Given a for loop, translate it into a while loop, and vice-versa

  4. Write a function that iterates through a provided string argument

  5. Given a description of pig latin, write a function that takes in a string argument and utilizes String#slice to translate the string into pig latin.

  6. Write a function that takes in an array of words and a string as arguments and returns a boolean indicating whether the string is located inside of the array. The function must use Array#indexOf.

  7. Define that an array literal is an ordered list of values defined by using bracket and individual values are read by indexing.

  8. Prevent code that can throw an exception from causing the program to crash. the terminal learning objectives for this lesson. When you complete this lesson, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for this lesson.

  9. Define a function that accepts a sentence string and two words as args. The function should return a boolean indicating if the sentence includes either word.

  10. Identify a pair of mutually exclusive conditions

  11. Given a for loop, translate it into a while loop, and vice-versa

  12. Write a function that iterates through a provided string argument

  13. Given a description of pig latin, write a function that takes in a string argument and utilizes String#slice to translate the string into pig latin.

  14. Write a function that takes in an array of words and a string as arguments and returns a boolean indicating whether the string is located inside of the array. The function must use Array#indexOf.

  15. Define that an array literal is an ordered list of values defined by using bracket and individual values are read by indexing.

  16. Prevent code that can throw an exception from causing the program to crash.


Control Flow - Conditional Statements

So far the code you've written has been pretty direct in it's intent. You can define functions and variables but, so far the functions you've created haven't been able to do that much for you yet. It's time to start writing functions that can do things conditionally by utilizing control flow.

In simple terms - control flow is the order in which instructions are executed within a program. One modifies control flow using control structures, expressions that alter the control flow based on given parameters. The control structures within JavaScript allow the program flow to change within a unit of code or a function.

This reading will be covering one of the two main control structures you will use time and time again - Conditional statements. Conditional statements are used to perform different actions based on different conditions.

When you finish this reading, you should be able to:

  • Write if, else if, if...else conditional statements.
  • Know that conditional statements can have only one if and one else statement.
  • Identify that conditional statements can be nested.

A Quick Word on Syntax

Before we get started we'll quickly go over the terms we'll be using to represent syntax.

  1. [ ] are square brackets
  2. { } are curly braces
  3. ( ) are parentheses

Writing Conditional Statements

Conditional Statements are the second fundamental control structure for writing JavaScript and are pretty straight forward. The simplest conditional statement is the if statement. An if statement has two parts, the test expression (the code that immediately follows the if which goes in parentheses), and the then expression (this code belongs in braces after the if expression). The then expression will only run when the if expression is truthy.

Here is an example of a simple if statement:

// this is the test expression
if (3 === 3) {
  // this is the then expression
  // this code will run if the above statement is true
  console.log("this is a three!");
}

The if statement above allows you to specify what should happen if your particular expression evaluates to true. You can chain additional test expressions onto this if statement by using a else if statement.

The syntax for else if is very similar as an if statement:

function mathFun() {
  let x = 2 + 3;

  if (x === 3) {
    console.log("we have a 3");
  } else if (x === 4) {
    // this code will run if the above statement is true
    console.log("we have a 4");
  } else if (x === 5) {
    // this code will run if the above statement is true
    console.log("we have a 5");
  }
};

mathFun(); // => "we have a 5"

The else if and if statements do not, however, provide the option to specify something else that should happen in the event that all of the above expressions evaluate to be falsey. The if...else statement reads just like English. The JS interpreter will execute the else statement if all the above conditions given are falsey. See below for an example:

function mathFun() {
  let x = 19;
  if (x === 3) {
    console.log("we have a 3");
  } else if (x === 4) {
    console.log("we have a 4");
  } else {
    console.log("I will return if everything above me is falsey!");
  }
};

mathFun(); // => "I will return if everything above me is falsey!"

You can chain an arbitrary number of else if statements but there can only be one if statement and one optional else statement. The if introduces the control structure and the else acts as a fail safe to catch everything that didn't meet the above conditions.

Only one then expression is ever executed in an if, if...else, or if...else statement. If one of the test expressions is truthy, then the result of its then expression is the result of the entire conditional statement:

let x = 3;
if (x === 3) {
  console.log("this will run");
} else {
  console.log("this will not run");
}

Additionally, you can nest conditional statements within each other but it will get hard to read pretty quickly and is discouraged:

function mathFun(x) {
  if (x === "math") {
    if (x === "math" && x[0] === "m") {
      if (x[1] === "a") {
        console.log("this got confusing fast");
      } else {
        console.log("that is not math!");
      }
    } else {
      console.log("that is for sure not math!");
    }
  } else {
    console.log("I will return if everything above me is false!");
  }
};

mathFun("math"); // => "this got confusing fast"

Review

  • Conditional statements allow us to control what actions should be taken based on a boolean (truthy or falsey) expression
  • In a chain of then expressions (if...else if...else), only one of the then expressions will be executed.
  • Conditional statements can have only one if and one else statement.
  • Conditional statements can be nested.

Mutually Exclusive Conditions

You have now learned how to write conditional statements. Now we'll talk a little bit more about how to write them using best practices.

When you finish this reading, you should be able to:

  • Identify a pair of mutually exclusive conditions.

When to use if statements

Say you are given the challenge to write a function that that will call another function named bigNumber if the given argument is greater than 100 or call a function named smallNumber if it the given argument is smaller. You could write a function to do that which would look like this:

function numberSeparator(number) {
  if (number < 100) {
    // number is smaller than 100 so we invoke smallNumber
    smallNumber();
  }
  if (number === 100) {
    // number is equal to 100 so we invoke smallNumber
    smallNumber();
  }
  if (number > 100) {
    // number is larger than 100 so we invoke bigNumber
    bigNumber();
  }
}

As you can probably tell the above function uses a lot of code to do a simple task. To be clear the function above would work for our aim - but it repeats itself. There is an age old principal for writing good code named DRY or Don't repeat yourself. As good programmers we always want our code to be clear, concise, and efficient.

A general rule of thumb is that if you are working with a condition that is mutually exclusive, meaning if one condition is true the other condition must be false, then you should use an if/else statement. You can also think of mutually exclusivity like a coin flip - it can be either heads or tails but not both.

Going back to the original problem at hand we can see it makes intuitive sense with the way the challenge is phrased: If the number is larger than 100 then we'll call bigNumber, otherwise we invoke is smallNumber.

So let's rewrite the above function to read a little more clearly:

function numberSeparator(number) {
  if (number > 100) {
    bigNumber();
  } else {
    smallNumber();
  }
}

// this also works
function numberSeparator(number) {
  if (number <= 100) {
    smallNumber();
  } else {
    bigNumber();
  }
}

Look at how much clearer that is! Writing good code is an art - devote yourself to becoming an artist!

Review

  • How to identify a pair of mutually exclusive conditions.
  • DRY - don't repeat yourself!

Control Flow - Looping

A quick reminder before we start - control flow is the order in which instructions are executed within a program. One modifies control flow using control structures, expressions that alter the control flow based on given parameters. This reading will be covering the second of the main control structures you will use time and time again - loops.

When you finish this reading, you should be able to:

  1. Know how to write a while loop and a for loop.
    • Know how to convert a for loop into a while loop
  2. Know that index variables conventionally start at zero.
  3. Explain what an iteration is.

Looping

Imagine you are at a friend's house and your friend has six dogs. Someone left the back gate open and all the dogs go out in the yard and get super muddy. Now your friend wants to clean their dogs but they only have one bathtub! You can't wash all the dogs at once. So the only option is to give the dogs a bath one at a time until they are all clean. When you start 0 dogs are clean and 6 dogs are dirty.

While there are still dirty dogs you still have a job to do. That is your condition - you will stop giving baths once all 6 dogs are clean. So after one bath you you have 1 clean dog and 5 dirty dogs. You've incremented(increased by one) your number of clean dogs. After each bath you check your condition again until you have 6 clean dogs so you know you can stop!

What we've described above is the idea of looping - setting a condition, executing an action, doing something to make sure our condition will be met eventually, and rechecking our condition before executing our next action.

Loops are a fundamental control structure for writing JavaScript. Loops will repeatedly execute a section of code while a condition is true. Loops are simple to write and incredibly powerful! There are many variations of loop but we will be covering the two most fundamental loops now - while loops and for loops.

While Loops

One of the simplest loops in JavaScript is the while loop. As with all loops, the while loop will execute a block of code as long as a specified condition is true. The while loop starts with the keyword while then states a condition in parentheses. The code in the following braces will be run until the above condition is met.

while (condition) {
  // code block to be executed
}

In the following example, the code in the loop will run, over and over again, as long as a variable (index) is less than 10:

let index = 0;

// this is the condition that will be checked every time this loop is run
while (index < 10) {
  console.log("The number is " + index);
  // this is common shorthand for index = index + 1
  index++;
}

The most important thing to remember when writing any loop is to always be working towards your condition. In the example above if we did not increment the index variable by 1 each time the loop ran then we would be stuck with what we call an infinite loop:

let index = 0;

// this is an infinite loop because our condition will never be met
while (index < 10) {
  console.log("The number is " + index);
  // if we do not increase the index then our condition is never met
  // Meaning this will run forever!
}

The above code will run until whatever interpreter you are using crashes.

Important Loop Knowledge

A quick word before we learn about the next loop.

The index is the traditional word for the variable that keeps track of how many times the loop has been run. Don't write loops with indices starting at one; you'll confuse other programmers and yourself. Indices have started at zero for a long time, and for good reason. It's much easier to use loops that start with an index of zero because Array and String indices also start at zero.

let array = [0, 1, 2];
let index = 0;

while (index < array.length) {
  console.log(
    "Both the index and the current array position are " + array[index]
  );
  index++;
}

In the above code we will do one loop for each digit in the Array above. We call each of those loops an "iteration". An iteration is the act of repeating a procedure, hence looping is an iterative technique. Each repetition itself is also called an "iteration." So you can use loops to iterate through Arrays and Strings.

For Loops

A for loop can be broken down into three sections:

  1. The initial expression which will be run once at the beginning of the loop.
  2. The condition which is checked every time the loop is run. If this condition is true the loop will run again. If this condition is false the loop will end.
  3. The loopEnd expression which will be run at the end of the loop before checking the condition again.
for (<initial expression>;<condition>;<loopEnd expression>)

The for loop is usually used with an integer counter:

for (let index = 0; index < 10; index += 1) {
  // the code inside this block will run 10 times
}

While the loopEnd expression is normally used to increase a variable by one per loop iteration, it can contain any statement, such as one that decreasing the counter, or increasing it by 2.

You can use the for loop to iterate through all kinds of things. Check out the example below for how to iterate through a String:

let testString = "testing";

// we can use the testString's length as our condition!
// Since we know the testString's index starts at 0
// and our index starts at 0 we can access each letter:
for (let index = 0; index < testString.length; index += 1) {
  let letter = testString[index];
  console.log(letter);
}

These are the most basic types of loops. If all else fails, you can always fall back on these two loops. All the other loop forms are just more convenient forms of these basic loop styles.

Translating From One Loop to Another

So far we have covered both while and for loops. Once you understand the concept of looping it's easy to translate one loop to another:

// these two do the exact same thing!
function forLoopDoubler (array) {
  // it is convention to shorten index to just i in most cases
  for (let i = 0; i < array.length; i++) {
    array[i] = array[i] * 2;
  }
  return array;
};

function forLoopDoubler (array) {
  let i = 0;
  while (i < array.length) {
    array[i] = array[i] * 2;
    i++;
  }
  return array;
};

forLoopDoubler([1, 2, 3]); // => [2,4,6]
whileLoopDoubler([1, 2, 3]); //=> [2,4,6]

Review

  • We can use a for or while loop to repeat a block of code repeatedly.
  • While the loop condition is true, we will execute another iteration of the loop.
  • When the loop condition is false, we will exit the loop.

The Array Type

This reading will be about one of JavaScript's global objects, the Array type. JavaScript arrays are used to store multiple values all within a single structure, much like a creating a list. Arrays can hold strings, integers and even other arrays! Arrays are incredibly useful for holding a bunch of different information all in one place.

When you finish this reading, you should be able to:

  • Write arrays using correct syntax
  • Identify that an array is an ordered list of values defined by using brackets
  • Use .length to obtain a count of the numbers of elements that comprise an array
  • Index an array to refer to a single value
  • Concatenate multiple arrays together

Using arrays

While coding you will find that you often find yourself needing to refer to a bunch of data at once. For instance, what if you wanted to refer to the entire English alphabet. Sure, you could create a bunch variables for each letter in the alphabet:

let a = "a";
let b = "b";
let c = "c";
let d = "d";
// and so on for way too long...

However this becomes cumbersome and unmanageable quickly. An Array is a data structure that solves this problem. Arrays are always wrapped in square brackets, [], and store their comma separated values in sequential order. Arrays in JavaScript are also very flexible: we can put elements into an array, replace elements in an array, and remove elements from the array.

So going back to our first example of containing the alphabet:

let alphabet = [
  "a", "b", "c", "d", "e", "f",
  "g", "h", "i", "j", "k", "l",
  "m", "n", "o", "p", "q", "r",
  "s", "t", "u", "v", "w", "x",
  "y", "z"
];

Indexing arrays

Calculating the length of an array

Since an array can container any number of values you will find it useful to count the number of values available to you using .length:

console.log([4, 7, 9].length); // => 3
console.log([1, 2].length); // => 2
console.log([].length); // => 0

Properly indexing an array

Arrays consist of multiple values all stored in sequential order. These value are numbered by indices starting at 0 (just like indexing a string!). So given the below example:

let numbersAndLetters = ["b", "z", 17, "cat"];

In the above numbersAndLetters array if we access numbersAndLetters at the index of 0 we get back the value of "b". If we access numbersAndLetters at the index of 1 we get "z", at the index of 3 we get 17, etc. We can specify which value we'd like to access in an array by using square brackets,[], and specifying an index:

console.log(numbersAndLetters[0]); // => "b"
console.log(numbersAndLetters[1]); // => "z"
console.log(numbersAndLetters[2]); // => 17
console.log(numbersAndLetters[3]); // => "cat"

Notice that even though the index at numbersAndLetters[3] has the value of a string with multiple characters ("cat") we return the entire value listed at that index.

Reminder: Arrays always start at the index of 0, not 1. This is the convention in programming. Additionally, indices should always be a number.

We can access a value in an array directly by providing an index for the value we'd like to access in that array (array[index]). See below for an example:

console.log(["a", "b", "c"][0]); // => "a"
console.log(["a", "b", "c"][1]); // => "b"
console.log(["a", "b", "c"][2]); // => "c"
console.log(["a", "b", "c"][3]); // => `undefined`

As we see in the code above, if we try to access an element at an index that is not inside the array, we get back undefined. This makes sense because there is no value at that given position!

The classic "off by one" error

Arrays are similar to strings in that both of their indices start at 0 instead of 1. Forgetting this fact can lead to some pretty confusing situations. Let's focus on an important distinction: the index of the last value of an array is always one less than its length.

console.log([4, 7, 9].length); // => 3
console.log([4, 7, 9][3]); // => undefined
console.log([4, 7, 9][2]); // => 9

In other words, although the length of [4, 7, 9] is 3, the index of the last value (9) is 2. A good rule of thumb of accessing the last index of an array is to find the length and then subtract one:

let testArray = [4, 7, 9];
let finalIndex = testArray.length - 1; // => (3 - 1) = 2
console.log(testArray[finalIndex]); // => 9

Working with arrays

Containing data in arrays

By packaging groups of related data into a single array, we gain the added benefit of being able to refer to that data as a single collection. Arrays don't have to just hold single characters- they are capable of holding entire strings, numbers, and even other arrays!

let wackyArray = [2, 17, "apple", "cat", ["apple"]];

console.log(wackyArray[0]); // => 2
console.log(wackyArray[1]); // => 17
console.log(wackyArray[3]); // => "cat"
console.log(wackyArray[4]); // => ["apple"]

Just think of all the possibilities of what you can store in a single array! However, just because you can doesn't mean you should. In practice we will almost always be storing similar kinds of data, that are coming from a common source (i.e. items in a shopping list, ID numbers, tasks on a todo list).

Using indexOf with arrays

We can also calculate the index of a given value within an array by using indexOf:

console.log([1, 3, 5, "apple", "jet"].indexOf(3)); // => 1
console.log([1, 3, 5, "apple", "jet"].indexOf(5)); // => 2
console.log([1, 3, 5, "apple", "jet"].indexOf("jet")); // => 4

// this won't be found in the array
console.log([1, 3, 5, "apple", "jet"].indexOf("potato")); // => -1

If we attempt to search for a value that is not present in an array, indexOf will return -1. This makes sense because we know that -1 is not a valid array index. The smallest index possible is 0!

Concatenation with arrays

As a reminder, concatenation is just a fancy word for joining things together into a single collection. Now, this is where arrays will differ from strings. The + operator only exists for numbers and strings. If you try to use the + on an array it will try to help you out by converting your arrays into strings.

console.log([1, 2, 3] + [4, 5, 6]); // => 1,2,34,5,6

JavaScript was just trying to help! However that is probably not what you meant to do. Good thing JavaScript has a seperate method for putting two array together. To concatenate arrays, we can use the aptly named .concat method:

console.log([1, 2, 3].concat([4, 5, 6])); // => [1, 2, 3, 4, 5, 6]

Remember

  • An Array is a data type that contains a list of in order values surrounded in square brackets [].
  • array.length returns the number of values in the array.
  • Each value of an array is associated with a number index; the first value of an array is at the index of 0.
  • We can use array.indexOf(value) to obtain the index of value within array; if value is not found, then -1 is returned.
  • We can use .concat to concatenate multiple arrays, combining them into a single array.

WEEK-01 DAY-3
Intermediate Functions {ignore=true}


Intermediate Functions Learning Objectives

Below is a complete list of the terminal learning objectives across all "Intermediate Function" lessons. When you complete these lessons, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for these lessons.

  1. Identify that strings are immutable and arrays are mutable

  2. Define a function using both function declaration and function expression syntax

  3. Utilize Array#push, #pop, #shift, #unshift to mutate an array

  4. List the arguments that can be used with Array#splice

  5. Write a function that sums up elements of an array, given an array of numbers as an argument

  6. Utilize Array#forEach, #map, #filter, #reduce in a function

  7. Define a function that takes in an array of numbers and returns a new array containing only the primes

  8. Define a function that takes in a 2D array of numbers and returns the total sum of all elements in the array

  9. Define a function that takes in an array of elements and returns a 2d array where the subarrays represent unique pairs of elements

  10. Define a function that takes in an array of numbers as an argument and returns the smallest value in the array; if the array is empty return null the terminal learning objectives across all "Intermediate Function" lessons. When you complete these lessons, you should be able to perform each of the following objectives. These objectives capture how you may be evaluated on the assessment for these lessons.

  11. Identify that strings are immutable and arrays are mutable

  12. Define a function using both function declaration and function expression syntax

  13. Utilize Array#push, #pop, #shift, #unshift to mutate an array

  14. List the arguments that can be used with Array#splice

  15. Write a function that sums up elements of an array, given an array of numbers as an argument

  16. Utilize Array#forEach, #map, #filter, #reduce in a function

  17. Define a function that takes in an array of numbers and returns a new array containing only the primes

  18. Define a function that takes in a 2D array of numbers and returns the total sum of all elements in the array

  19. Define a function that takes in an array of elements and returns a 2d array where the subarrays represent unique pairs of elements

  20. Define a function that takes in an array of numbers as an argument and returns the smallest value in the array; if the array is empty return null


Function Expressions

You may have noticed that we've been writing many functions so far in the course! We will continue to do so since functions are the building blocks of the eventual applications that we will build. That being said, let's begin to broaden the way we think about functions. In particular, we'll want think of functions as expressions that we can store in variables - just like our classic data types of number, string, boolean, array, and object!

When you finish this article, you should be able to:

  • identify functions as first-class objects in JavaScript
  • define a function using function expression syntax

Functions as first-class objects

JavaScript is well known for being a programming language that treats functions as "first-class objects". This fancy talk means that you can treat a function as a "normal" value by storing it in a variable. We'll leverage this key concept in very clever ways later in the course. For now, let's begin with a simple example that shows the "first-class object" nature of functions:

let calculateAverage = function(a, b) {
  return (a + b) / 2;
};

console.log(calculateAverage(10, 20)); // 15

In the code snippet above, we define the calculateAverage by assigning a variable to contain the function's definition. By doing this, the variable's name is effectively the function's name. So to call the function, we simply refer to the variable name. Note that we do not write the function's name after the function keyword, where we normally would. We will refer to this new way of defining functions as function expression syntax and the classic way of defining functions as function declaration syntax. In general, we can define functions using either syntax:

// function declaration syntax
function myFunctionName(arg1, arg2) {}

// function expression syntax
let myFunctionName = function(arg1, arg2) {};

In the coming sections, we'll highlight moments when we'll prefer one syntax over the other. For now, get acquainted with the new syntax as it is something you'll be seeing a lot of as a programmer!

A peek under the hood

Perhaps you're finding it tough to understand what it means for a variable to contain a function - it is indeed a very abstract idea for new programmers. Let's draw a comparison. We know that when we assign an expression to variable, the expression first evaluates to a single value, which we then store in the variable name:

let myNum = 4 + 4;
console.log(myNum); // prints 8
console.log(myNum * 3); // prints 24

In the same way we can treat a function definition as an expression that evaluates!

let myFunc = function() {
  console.log("I'm a function");
};

console.log(myFunc); // prints [Function: myFunc]
myFunc(); // prints "I'm a function"

Looking at the snippet immediately above, you'll notice that when we print the myFunc variable directly, without calling the function with parentheses, JavaScript simply says the variable contains a function named myFunc ([Function: myFunc]). You can truly imagine a function as a value that we can store and use as we please.

The term anonymous function may also be used to describe a function expression before it is assigned to any variable. Following the example above, we'll use the word anonymous function to describe the function expression before the assignment to the myFunc variable is complete. Once the assignment is complete, it would be silly to refer to myFunc as an anonymous function because an anonymous function has no name.

Remember

  • functions can be stored in variables; just like any other values in JavaScript!

Two-Dimensional Arrays (2D Arrays)

Time to broaden our understanding of arrays! We've already explore the fundamentals of arrays. Mainly, we can store any type of data we please as elements of an array and even mix types together. However, what happens if we store an array as an element of an array?

When you finish this article, you should be able to:

  • index into the inner elements of a 2D array
  • use nested loops to iterate through a 2D array

Multidimensional Arrays

When we store arrays as elements of other arrays, we refer to those structures as multidimensional arrays. If the "depth" of the nested arrays is at exactly 2 (an outer array containing inner arrays), then we'll refer to it as a two-dimensional array:

let twoDimensional = [["a", "b", "c"], ["d", "e"], ["f", "g", "h"]];

console.log(twoDimensional[1]); // [ 'd', 'e' ]
console.log(twoDimensional[1][0]); // 'd'

let subArr = twoDimensional[1];
console.log(subArr[0]); // 'd'

Note that indexing the outer twoDimensional array will return an element like usual, it's just that element happens to be another array. To gain access to the innermost elements, we simply need to apply another set of indexing brackets!

If we style our 2D arrays nicely so that each subarray is on a new line, we can interpret the double indices as [row][column]:

let twoDimensional = [
  ["a", "b", "c"],
  ["d", "e"],
  ["f", "g", "h"]];

// get the element in the 0th row, 2nd col:
console.log(twoDimensional[0][2]); // 'c'

Iterating through 2D Arrays

Since a 2D array is just an array of arrays. We'll need to use a loop within a loop to iterate through a 2D array:

let array = [["a", "b", "c"], ["d", "e"], ["f", "g", "h"]];

for (let i = 0; i < array.length; i++) {
  let subArray = array[i];
  console.log(subArray);

  for (let j = 0; j < subArray.length; j++) {
    console.log(subArray[j]);
  }
}

In the nested loops above, the i index refers to the current "row" and the j index refers to the current "column". It's worth noting that since the inner subArrays have different length, we'll want to specifically reference the length of that subarray in our inner loop condition j < subArray.length. The code above will print:

[ 'a', 'b', 'c' ]
a
b
c
[ 'd', 'e' ]
d
e
[ 'f', 'g', 'h' ]
f
g
h

When is a 2D array practical?

As a preview of things to come let's briefly mention when you'll find a 2D array useful in your future projects. Imagine how'd you represent a "grid":

  • tic-tac-toe (3x3 grid)
  • chess (8x8 grid)
  • sudoku (9x9 grid)
  • excel (a sheet is an arbitrarily sized 2D array)

Remember

  • an array can contain arrays as elements, we call this a 2D arrays
  • to iterate through a 2D array, used nested loops

Mutability in JavaScript

So far in the course we've explored a handful of methods that manipulate data. We'll be growing our arsenal of methods further overtime, so we'll want to gain awareness for exactly how we should expect these methods to manipulate the data we give them. To this end, let's analyze which methods will modify existing data and which methods do not. We refer to this concept as mutability.

When you finish this article, you should be able to:

  • Explain what "mutability" is
  • Correctly label JavaScript data types as immutable or mutable

What is mutability?

At its face value, mutability is a simple concept. You may be familiar with the word mutation, which refers to a alteration (usually in DNA). Something that is mutable can be changed, while something that is immutable is unchanging and permanent. To illustrate this concept, we'll begin with strings and arrays. We've spent some time with these two data types and by now we recognize that the two types share many similarities. Both have indices, length, and even share common methods like slice. However, they differ greatly in their mutability:

let myArr = ["b", "e", "a", "m"];
myArr[0] = "s";
console.log(myArr); // 'seam'

let myStr = "beam";
myStr[0] = "s";
console.log(myStr); // 'beam'

Above we have shown that we can assign a new element to an index of an array, but we cannot assign a new character to an index of a string. In other words, arrays are mutable, but strings are immutable.

An implication of this discovery is that there are some array methods that will modify an existing array but zero methods that will modify an existing string. Methods that manipulate string data typically return a new string and never modify an existing one. A prime example is toUpperCase:

let word = "piñata";
let newWord = word.toUpperCase();
console.log(word); // 'piñata'
console.log(newWord); // 'PIÑATA'

Above, notice that the toUpperCase method returns a capitalized version of the string, but does not change the original string. It's also worth noting that not every array method will mutate. For example, the slice method does not mutate for both strings and arrays. As we learn about methods in the future, we'll be certain to note what mutates and what does not.

Mutable or immutable, that is the question

Now that we have a grasp of mutability, let's take inventory and identify JavaScript's data types as mutable or immutable.

Mutable

  • array
  • object (we'll learn these soon)

Immutable

  • number
  • string
  • boolean

A quick way to remember the above list is to identify that the composite types (the types that can contain multiple values) of array and object are mutable. The remaining "simpler" types of number, string, and boolean are immutable.

The mutability misconception

Maybe you are having a tough time believing what we have just claimed. We don't blame you, you've probably heard the saying that change is the only constant in the universe. Let's debunk a common argument to turn you into a believer. The skeptical programmer may use this as an argument to show that numbers are mutable:

let myNum = 42;
myNum += 8;
console.log(myNum); // 50

Because the myNum variable now contains 50 where it once contained 42, it may seem we have mutated the number, but this is not truly the case. Recall that myNum += 8 is shorthand for myNum = myNum + 8. Since the right hand side of the assignment evaluates first, we are simply taking the new number of 50 and reassigning it to the myNum variable. This reassignment of a variable name is not a mutation of the original number.

Remember

  • data types that can be changed are mutable, those that cannot be changed are immutable
  • arrays and objects are mutable
  • numbers, strings, and booleans are immutable

Array Splice

Time to a learn yet another array method! The Array#splice method deserves its own reading because of how versatile it is. Feel free to use this article as a quick reference; let's jump right in.

When you finish reading this article, you should be able to:

  • list all possible arguments that can be used with the Array#splice method

Notation

For clarity in this article and moving forward in the course, we'll be notating methods with # to clarify how they should be called. For example, Array#splice refers to the method that should be called on an array, arr.splice() where arr is some array variable. Likewise String#toUpperCase refers to the method that should be called on a string, str.toUpperCase() where str is some string variable. We'll opt to refer to methods using this notation because some methods can be called on multiple data types, such as Array#slice and String#slice.

What can Array#splice do?

Before we explore the nitty-gritty details of the Array#splice method, the first thing to be aware of is that the method will mutate the array that it is called on. That is, Array#splice will modify the existing array and not return a new array.

Using splice to remove

The usage of the Array#splice method is easy to mix up because it can be used to remove or insert elements into an array. That's right - it can perform "opposite" operations, even at the same time! For now, we'll begin by only removing elements from an array:

let colors = ["red", "yellow", "blue", "green", "orange", "brown", "gray"];
let returnVal = colors.splice(2, 3);
console.log(colors); // [ 'red', 'yellow', 'brown', 'gray' ]
console.log(returnVal); // [ 'blue', 'green', 'orange' ]

The first two arguments for splice correspond to 1) the target index and 2) how many elements to remove. The call colors.splice(2, 3), will remove the next three elements beginning at index 2. This means that the elements at indices 2, 3, and 4 are removed.

Note that splice returns an array containing the elements that were removed and also has the effect of removing the elements from the original array, mutating it in-place.

Using splice to insert

To use the splice method to insert new elements into an array, we can pass in any number of additional arguments representing the values to insert:

let colors = ["red", "yellow", "blue"];
let returnVal = colors.splice(1, 0, "RebeccaPurple", "CornflowerBlue");
console.log(colors); // [ 'red', 'RebeccaPurple', 'CornflowerBlue', 'yellow', 'blue' ]
console.log(returnVal); // []

The method call colors.splice(1, 0, 'RebeccaPurple', 'CornflowerBlue') translates to "target index 1, remove the next 0 elements, then insert 'RebeccaPurple' and 'CornflowerBlue'."

Using splice like a pro

Naturally, we can combine these two functionalities! Say we wanted to target index 2, remove the next 3 elements, then insert 'Gainsboro', 'Ivory', and 'Khaki':

let colors = ["red", "yellow", "blue", "green", "black", "beige"];
let removed = colors.splice(2, 3, "Gainsboro", "Ivory", "Khaki");
console.log(colors); // [ 'red', 'yellow', 'Gainsboro', 'Ivory', 'Khaki', 'beige' ]
console.log(removed); // [ 'blue', 'green', 'black' ]

Bam. What a versatile method! Always feel free to reference the documentation for the method when you are struggling to remember its usage:

Remember

  • Array#splice has two required arguments
    • the target index
    • the number of elements to remove beginning at that target index
  • Array#splice can also take in any number of values to be inserted at the target index

String#split and Array#join

We've seen previously that strings and arrays share many similar properties. For example, strings and arrays both have a length and can have multiple indices. Because of this, you may find it useful to "convert" between the two types.

When you finish this article, you should be able to:

  • use the String#split method to turn a string into an array
  • use the Array#join method to turn an array into a string

String#split

The String#split method is called on a string and accepts a "separator" string as an argument. The method will return an array where the elements are the resulting substrings when we cut at the "separators":

let sentence = "follow the yellow brick road";
let words = sentence.split(" ");
console.log(words); // [ 'follow', 'the', 'yellow', 'brick', 'road' ]
console.log(sentence); // 'follow the yellow brick road'

Note that the original string is not mutated, rather a new array is returned. A common pattern is to split a sentence string on a space (' '), but you can split on any separator as you see fit:

let sentence = "follow the yellow brick road";
console.log(sentence.split(" ")); // [ 'follow', 'the', 'yellow', 'brick', 'road' ]
console.log(sentence.split("the")); // [ 'follow ', ' yellow brick road' ]
console.log(sentence.split("o")); // [ 'f', 'll', 'w the yell', 'w brick r', 'ad' ]

A pattern you may find useful is that when you split on a separator string, it is guaranteed that that separator will not be in the resulting array, effectively removing it. See the example of sentence.split('the') above. This may come in handy, so keep it in mind!

Array#join

The Array#join method is called on an array and accepts a "separator" string as an argument. The method will return a string where elements of the array are concatenated together with the "separator" between each element:

let words = ["run", "around", "the", "block"];
let sentence = words.join(" ");
console.log(sentence); // 'run around the block'
console.log(words); // [ 'run', 'around', 'the', 'block' ]

console.log(words.join("_")); // 'run_around_the_block'
console.log(words.join("HI")); // 'runHIaroundHItheHIblock'

Array#join does not mutate the original array, instead it will return a new string.

A clever combination

It's pretty evident that String#split and Array#join are "opposite" methods. That is:

  • we use split to turn a string into a array
  • we use join to turn an array into a string

By combining these two methods we can accomplish some cool behavior:

let str = "I don't know what I want to eat";
let newStr = str.split("I").join("we");
console.log(newStr); // 'we don't know what we want to eat'

Whoa! We were able to replace every substring "I" with the substring "we". We know that the line str.split('I').join('we') evaluates from left to right. This means that the split will cut the string wherever there is an 'I', leaving a gap where the 'I's were. Then, the join will fill those gaps with 'we's.

Remember

  • we can use String#split and Array#join to convert between strings and arrays
  • both methods do not mutate their input

WEEK-01 DAY-5
Control Flow {ignore=true}


Determining Types

Sometimes you want to know the type of value store in a variable so that you can safely do things with it. If your function expects an array in its parameter but gets a number, you can't call the map method on that!

In this article you will learn how to figure out if a value in a variable is

  • A string
  • A number
  • A function
  • An array

The typeof operator

Not all operators in JavaScript require two arguments like the + operator for addition, the = for assignment, and the % operator for modulo division. Those are all called binary operators because they take two (bi-) operands, or things that are operated on.

JavaScript kindly gives you the operator typeof which acts on a single value. Operators that take only one operand are called unary operators because "u only give them one value!" (That's a joke. "uni-" or "una-" is one.)

Here are some examples of what you'd expect to see with the typeof operator.

let s = 'This is a string';
console.log(typeof s);    // 'string'

let n = 6.28;
console.log(typeof n);    // 'number'

let sum = function (a, b) {
  return a + b;
}
console.log(typeof sum);  // 'function'

Note that the value returned from the typeof operator is a String data type. So, if you want to check if a value is a number, you could do this.

if (typeof n === 'number') {
  // It is a number. Do some maths!
} else {
  console.log('I really wanted a number. :-(');
}

How to tell if a value is an array

Unfortunately, due to a really old bug in the way that JavaScript works, a bug that no one can fix because people wrote code that relies on the bug for decades, you cannot use the typeof operator to figure out if something is an array.

let a = [1, 2, 3];
console.log(typeof a);  // 'object'

Gee, JavaScript. That's not helpful. Thanks.

Luckily, it only took 12 years for JavaScript to include a way to test if a value is an array. To do so, you use the Array.isArray method like this.

let a = [1, 2, 3];
Array.isArray(a);  // true

let n = 6.28;
Array.isArray(n);  // false

let f = function () {}
Array.isArray(f);  // false

Practical use in "real" code

Oddly enough, you won't see a lot of code in real-world applications testing if a value is one type or another. A lot of JavaScript functions just assume that they will get arguments of the right kind because the parameter names imply what kind of value to pass in. For example, the following function has a parameter named sentence.

function reverseTheSentence(sentence) {
  // ... code here ...
}

Most developers will know that the function probably wants sentence to be a string value. They just won't pass in an array or number or ... well, anything other than a string. Because that's just not polite. They'd expect any other kind of value to cause the reverseTheSentence to malfunction. Only when you know that people that don't respect your code will use it should you add in some kind of check like this.

function reverseTheSentence(sentence) {
  if (typeof sentence !== 'string') {
    // Tell the developer they are using
    // the function wrong.
  }
  // ... code here ...
}

Remember

This article has shown you two ways to determine if a value is a kind of type:

  • the typeof operator to use to test if a value is a number, a string, or a function; and,
  • the Array.isArray method to check if a value is an array.

Use them as much (or as little) as you need!


The Null Type (And Undefined)

You've met numbers and string, Booleans and arrays. There's another type often used in JavaScript: the Null type. And, it's a special type.

In this article, you will learn about the Null type, its value, and how to work with it in JavaScript.

A type with only one value

You have seen that the String type can have an "infinite" number of values (within the limits of your computer memory). For example, the String type represents any of the following values.

// Examples of values with the String type
'hello, world'
"this is a string"
`Where is my pudding?`
''
'A really long string.........................................................'

The Number type also has this aspect. Any number that you can reasonable express in JavaScript has the Number type.

// Examples of values with the Number type
-100
99
6.28
Infinity

You also know about the Boolean type. It can have only two values.

// The only two values of Boolean type
true
false

There are not more Boolean values. You can't dream up more. There are only two, those two.

The Null type has one and exactly one value.

// The only value that has the Null type
null

It's just that word: null. No quotation marks. No other fancy things. Just null.

The meaning of null

This is a harder subject to tackle because it's a philosophical subject. Many people ask, "What does the value of null mean in a program?" There are a couple of answers that programmers give to this. None of them are wrong. None of them are right. They just are. In the presence of null, the code you write determines which of the following meanings null has.

  • The value null means the absence of a value or no value
  • The value null means an unknown value
  • The value null is a nuisance and I hate it and wish it were never invented

During your software programming career, you will likely have all three of those opinions, sometimes at the same time. Let's take a look at some examples to try to figure this out.

The absence of a value

Let's say you wrote a function that splits a string into words, reverses them, and puts them back together in reverse order. You can do that with the methods

  • String#split link;
  • Array#reverse link; and,
  • Array#join link.

That function could look like this.

function reverseTheSentence(sentence) {
  let parts = sentence.split(' ');
  parts.reverse();
  return parts.join(' ');
}

That's great! It works! But, what happens if someone doesn't care about what your function and just decides to pass in something that's not a string? It would make sense that reversing something that is not a string should lead to no value, the absence of a value, because the input to the function doesn't make sense. In that case, you can just return a null because there is no value that the function can return that would make sense.

function reverseTheSentence(sentence) {
  if (typeof sentence !== 'string') {
    return null;
  }
  let parts = sentence.split(' ');
  parts.reverse();
  return parts.join(' ');
}

An unknown value

There are a lot of programmers that will argue that null cannot be an unknown value. "The value is known!" they'll exclaim. "The value is 'null'! It's known! It's 'null'! Stop saying it's not known!"

There are programmers that vehemently disagree with that.

shrug

Checking if a value is null

If you had hoped that you could use the typeof operator to check if a value is null, then you're out of luck.

// Useless code.
console.log(typeof null);  // 'object'

Silly JavaScript. Instead of using the typeof operator, you can just compare the value to null because there is only one value of the Null data type and it's always null. Take a look at the following code and figure out what you think it will produce.

let a = [];
let x = null;

if (a === null) {
  console.log('a is null');
} else if (x === null) {
  console.log('x is null');
}

Oh, and there's that undefined value, too

Just like the null value that is the only value of the Null data type, there is undefined which is the only value of the Undefined data type.

If you're asking yourself, "Wait, if 'null' is no value or the absence of a value, then what the heck does 'undefined' mean?", well you're not the only one.

Have a look at this code.

let value;

value = 6.28;
console.log(value);

You probably will not be surprised to see that it will print out "6.28" because that's the value of value. But, what if you did this? What does that new console.log print?

let value;
console.log(value); // <- what does this print?

value = 6.28;
console.log(value);

If you guessed that it prints "undefined", you're right! When you declare a variable, it's very first value is undefined. Most of the time, though, you'll just immediately set it to a value.

let value = 6.28;

So, an uninitialized variable has the value undefined which is the only value of the Undefined data type. To test for it, you can use the typeof operator or the strict equality operator. Using the strict equality operator is the more common way to do that, now.

// Test if a value is undefined
if (value === undefined) {
  // do a thing
}

// You can also do it this way, but
// it is considered passé.
if (typeof value === 'undefined') {
  // do a thing
}

What happens when...

Interestingly enough, all functions actually do return values. Have a look at this function. What value does it return? (Not a trick question.)

function returnsTrue() {
  return true;
}

Yes, it returns the value true. But, what about this function?

function returnsWhat() {
  return;
}

There's a return statement there but it does not specify a value. If there is not value specified, what do you think this function returns? Try putting the function definition above and the code below into a code runner and seeing what happens.

console.log(returnsWhat());

One you figure that out, try the same experiment but with this function. What do you think it returns. It doesn't even have a return statement in it!

function whatIsThis() {
}

Remember

There is a special value in JavaScript represented as null which means "no value" or "unknown value". It is the only value of the Null data type. You can check that a value is null by using the strict equality operator x === null.

The value undefined is used by JavaScript for variables that have not been assigned a value. Also, functions that do not return an explicit value return the value undefined. You can test if a value is undefined by using the strict equality operator x === undefined.


Catch Me If You Can

Sometimes bad things happen to good programs. Some person may enter some badly formatted information. Another computer may try to attack your program by sending it wonky data. A network could go down. A file can become corrupt. When this happens, your running software will have some errors. This article is about how you can recover from those errors with "structured exception handling".

In this article you'll learn the answers to:

  • What is structured exception handling?
  • How can I do that in JavaScript?
  • How can I make my own errors?
  • What else can I do with it?
  • Shouldn't I just always do this?

Structured exception handling

Oddly enough, there are very few error-handling mechanisms in use, today, in all programming languages. There are really only three ways that programming language provide structured exception handling.

  • Error or error code reporting as found in languages like C and Go
  • Continuable exceptions as found in Common Lisp
  • Stack unwinding which is found in almost every modern programming language including JavaScript and Python

In the stack-unwinding paradigm, which is the one you'll use in JavaScript, when an error occurs, the JavaScript interpreter (the thing running your JavaScript code) looks for some kind of handler for that error. It has a very specific way of searching for those handlers. The way JavaScript searches for the handlers is very similar to the way it happens in Python, C++, Java, C#, and a lot of other languages. Once you learn it, here, you will be able to handle errors when writing code in all of those languages.

Try and catch

Say you have some code that may have an error. For example:

function sumArray(array) {
  let sum = 0;
  for (let i = 0; i < array.length; i += 1) {
    sum += array[i];
  }
  return sum;
}

If someone calls the above function with the code sumArray(null), then they will get the error because the for loop is trying to get the length property of the array parameter which is null.

TypeError: Cannot read property 'length' of null

To prevent this from ruining your program, you wrap code that may have an error in a try block. Now, you've seen other blocks already: if blocks, for blocks, function blocks. Basically, if there are curly braces around some lines of code, that's a code block of some kind. A try block is just some curly braces with the try keyword.

// THIS IS AN INCOMPLETE BLOCK OF CODE
function sumArray(array) {
  let sum = 0;

  // The try block wraps the for loop. If some
  // error occurs, the try block will give you
  // a chance to react to it so that the program
  // doesn't terminate.
  try {
    for (let i = 0; i < array.length; i += 1) {
      sum += array[i];
    }
  } // needs something more here

  return sum;
}

The try block tells JavaScript that it needs to watch the code inside the curly braces for an error. Now, you have to tell JavaScript what to do when there is an error. You do that in the catch block that should immediately follow the try block. The catch block accepts a single parameter that (usually) contains an object that describes the error that occurred. In the case of the sumArray method, if an error occurs, you could return the value undefined rather than letting an error terminate your program. You could also log the error to the "error" output.

function sumArray(array) {
  let sum = 0;

  try {
    for (let i = 0; i < array.length; i += 1) {
      sum += array[i];
    }
  } catch (e) {
    console.log(e);
    return null;
  }

  return sum;
}

sumArray(null);

Just to state it, again: the catch block runs when an error occurs in the try block. If no error occurs in the try block, the catch block does not run.

That (e) after the word catch is a variable that contains any error that was thrown and caught by this try-catch block. It doesn't have to be named e.

function sumArray(array) {
  let sum = 0;

  try {
    for (let i = 0; i < array.length; i += 1) {
      sum += array[i];
    }
  } catch (pancakes) {
    console.log(pancakes);
    return null;
  }

  return sum;
}

sumArray(null);

Here is the same code but, instead of a variable named "e", there is a variable named "pancakes". Now, if an error is thrown, the variable "pancakes" will contain it. By long-standing tradition, the variables used with the catch block are normally "e", "err", or "error".

// CODE SNIPPET, WILL NOT RUN
} catch (e) {
// CODE SNIPPET, WILL NOT RUN
} catch (err) {
// CODE SNIPPET, WILL NOT RUN
} catch (error) {

Now, when you run the code sumArray(null), you should see something like the following, if you run it in the online code editor.

TypeError: Cannot read property 'length' of null
  at sumArray (/tmp/file.js:5:31)
  at Object.<anonymous> (/tmp/file.js:16:1)
  at Module._compile (internal/modules/cjs/loader.js:1158:30)
  at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
  at Module.load (internal/modules/cjs/loader.js:1002:32)
  at Function.Module._load (internal/modules/cjs/loader.js:901:14)
  at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
  at internal/main/run_main_module.js:18:47

In that code sample, after the sumArray(null) call, the lines that begins TypeError is the error that occurred. The next 10 lines are what is known as a stack trace. You'll end up seeing these a lot, most likely, as you continue your career in software programming. This is the first line in understanding errors in your code. The stack trace shows on the first line where the error occurred: sumArray (/tmp/file.js:5:31) means that it occurred in the sumArray method on line 5 of the content, at character 31. If you open up one of the coding problems, paste that code block in, and run it, you'll see similar output in the output block.

The last line undefined is the return value of the sumArray(null) invocation that now happens when an error occurs.

That is how the so-called try-catch block works.

How can I make my own errors?

To create your own errors with structured exception handling, you first need to create an error object with the message that describes the error. Then, you need to "throw" the error. That code would look like either of these two lines, the only difference being the new keyword. They both work exactly the same.

throw Error('this happened because I wanted it to');
throw new Error('this happened because I wanted it to');

What else is there?

Turns out that you can have one more block on the try-catch block. It is the finally block. The finally block runs whether or not an error occurs. It always runs.

function sumArray(array) {
  let sum = 0;

  try {
    for (let i = 0; i < array.length; i += 1) {
      sum += array[i];
    }
  } catch (e) {
    console.log(e);
    return null;
  } finally {
    console.log('you will always see this.');
  }

  return sum;
}

How do I best use this?

At this point, you may be asking yourself, "Self, since errors can occur everywhere, shouldn't I just wrap all of my code in these try-catch blocks?"

No. No, you shouldn't.

Every try-catch block introduces another slow-down in your code. If you're writing code that you want to run as fast as possible, then you write as few try-catch blocks as possible. Also, it makes the code pretty cluttered with all of the indentation and curly braces. When at all possible, you should write defensive code which checks for bad values before errors get thrown in your code. Rather than using a try-catch block in the sumArray function, you could defend against bad values of the array parameter like so.

function sumArray(array) {
  if (array === null) {
    return null;
  }

  let sum = 0;
  for (let i = 0; i < array.length; i += 1) {
    sum += array[i];
  }
  return sum;
}

Review

The try-catch-finally block is a mechanism to handle errors in your code. You should not wrap all of your code in these blocks because it can seriously degrade the performance of your application. Instead, only wrap those portions of the code that you want to guard against throwing exceptions.

A better choice, in JavaScript and all programming languages, is to be defensive about your programming and choose to check that the value that you have has the functionality that you desire by adding code like

if (value !== undefined) {}
if (value !== null) {}