Loops Flashcards
Do a block. Then Continue doing it while a condition is true
do {
}
while ( )
If a condition is true keep doing a block with local scope conditional and variables
for (let i = 0; i < 4; i ++) {
}
If a condition is true, do a block. Continue if still true
while ( ) {
}
Do a function for each array element (method)
myArray.forEach(element => )
Return a new array based off another’s elements and a callback function (arrays, not array like objects).
What’s the one for array-like objects?
myArray.map(element => )
Array.from() is the one for array-like objects
End a loop
Stop and loop and return something
Skip to the next loop iteration
break
return
continue
Return the type of data
(will return a string)
typeof input
Return boolean whether argument is an array or not
Array.isArray( )
Do a block with every element in an array or character in string.
for (let (…element) of …)
for (let variableName of iterableArrayOrFunction) {
statement
}
Do a block with each key in an array, object, or function
for (let (…key) in …
What elements of:
for (let i = 0; i < 4; i ==) {
}
Can be skipped
all of them, you can do:
for (;;) {
}
it will just loop endlessly
Expected output // 1, then 3, 5, 7, 9
Both of these will give the output:
for (let i = 0; i < 10; i++) { if (i % 2 == 0) continue; alert(i); }
vs
for (let i = 0; i < 10; i++) { if (i % 2) { alert( i ); }
}
But which should you use and why?
The first one is nicer because it reduces nesting
nesting bad because it can be hard to read??
What does break do here?
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, ''); break; } }
only breaks from first loop
Break from multiple nested loops.
Use labels: outerLabel: for (let i = 0; i < 3; i++) { //(*)
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// if an empty string or canceled, then break out of both loops if (!input) break outerLabel; // (*)
// do something with the value... } } alert('Done!');
Can you use arbitary expressions in switch statements? For example: switch () { case b + 1 : console.log('etc'); break; }
Yes
Do switch statements use equality or strict equality?
Strict
What is returned from:
(i > 5) ? alert(i) : continue;
It throws an error its not a loop so continue doesn’t do anything
What’s the difference between:
for (let… in …)
for (let… of …)
for (let …)
for (let… in …) - The for…in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties.
for (let variableAsString in object) {
statement
}
for (let... of ...) - a loop iterating over iterable objects, including: built-in String, Array, array-like objects (e.g., arguments or NodeList), TypedArray, Map, Set, and user-defined iterables. for (variable of iterable) { statement } for (let (...element) of ...) for (let variableName of iterableArrayOrFunction) { statement }
for (let …) - The for statement creates a loop that consists of three optional expressions, enclosed in parentheses and separated by semicolons, followed by a statement (usually a block statement) to be executed in the loop.
for ([initialization]; [condition]; [final-expression])
statement
can do:
for (;;) {
}
What is logged to console from:
let myObject = { 1: 1, 2: 2, 3: 3, }
for (value of myObject) {
console.log(value);
}
Error,
myObject is not iterable
What must a value have for it to be iterable (by the for of loop for instance)?
It must have the ‘next’ method
a Symbol.iterator key that returns an object with the next() method that returns an object { done: true or false, value: the value }
What is the algorithm for the for of loop? (4 steps)
- When for..of starts, it calls that method:
range[Symbol.iterator] = function() {
return Iterator object…
once (or errors if not found). The method must return an iterator – an object with the method next. - Onward, for..of works only with that returned object.
- When for..of wants the next value, it calls next() on that object.
- The result of next() must have the form {done: Boolean, value: any}, where done=true means that the loop is finished, otherwise value is the next value.
Create an iterable object that will receive the values 1 through 5 from this object: let range = { from: 1, to: 5 };
let range = {
from: 1,
to: 5
};
// 1. call to for..of initially calls this range[Symbol.iterator] = function() { // ...it returns the iterator object:
// 2. Onward, for..of works only with the iterator object below, asking it for next values
return {
current: this.from,
last: this.to,
// 3. next() is called on each iteration by the for..of loop next() {
// 4. it should return the value as an object {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; };
// now it works! for (let num of range) { alert(num); // 1, then 2, 3, 4, 5 }
The following is incorrect.
T or F
let range = {
from: 1,
to: 5
};
range[Symbol.iterator] = function() {
return {
current: this.from,
last: this.to,
next() { if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; };
False
An iterator is created by the call to the Symbol.iterator method
For the iteration to continue the symbol.iterator method has to return an object with the next() method on it.
Adding Symbol.iterator inside an object does not make the object properties iterable. But instead calls upon another object to set the rules of iteration
T or F
True
BUT The iterator looks for the next() method in whatever object is returned from myObject.Symbol.iterator.
So you can return the outer object in range.Symbol.iterator (return this) and have the next() method there.
let range = {
from: 1,
to: 5,
Symbol.iterator {
this.current = this.from;
return this;
},
next() { if (this.current <= this.to) { return { done: false, value: this.current++ }; } else { return { done: true }; } } };
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
The downside is that now it’s impossible to have two for..of loops running over the object simultaneously: they’ll share the iteration state, because there’s only one iterator – the object itself. But two parallel for-ofs is a rare thing, even in async scenarios.
You can’t have infinite iterables in an object
T or F
False
For instance, the range becomes infinite for range.to = Infinity.
You can’t run a for of loop on a string
T or F
False
Call an iterator on this string manually using while(true) and then controlling everything else with the string's iterator. let str = "Hello"; (reverse card for hint. Then scroll down for solution)
hint: Set a variable to str's iterator function (to the object that contains next()) Set a variable to the value of next() (the {done: value:} object) Set a conditional based on 'done' to break the loop. Then do whatever you want to happen for each character . . . . . . . . . . . . . . . . let iterator = str[Symbol.iterator]();
while (true) { let result = iterator.next(); if (result.done) break; alert(result.value); // outputs characters one by one }
What’s the difference between an iterable and an array-like object?
Iterables are objects that implement the Symbol.iterator method, as described above.
Array-likes are objects that have indexes and length, so they look like arrays.
Strings are iterable and array-like objects
T or F
True. Strings are both iterable (for..of works on them) and array-like (they have numeric indexes and length).
What makes an object Array-like?
They have indexes and length, so they look like arrays.
What makes an object iterable?
They implement the Symbol.iterator method and have the next() method
“range” is iterable and array-like T or F let range = { from: 1, to: 5 };
range[Symbol.iterator] = function() {
return {
current: this.from,
last: this.to,
next() { if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; };
False
range is iterable but not array-likebecause it does not have indexes or length
arrayLike is array-like
T or F
let arrayLike = { 0: "Hello", 1: "World", length: 2 }
True. It has indexes and length
Array.from() can be used on each of the following T or F:
let arrayLike = { 0: "Hello", 1: "World", length: 2 };
let arrayLike2 = { 0: "Hello", 1: "World", };
let arrayLike3 = { from: 1, to: 5, arrayLike3[Symbol.iterator] = function() { return { current: this.from, last: this.to, next() { if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; };
T
F
T
Array.from() works on anything iterable and/or array-like
What kinds of objects can Array.from() work with?
Array.from() works on anything iterable or array-like
When you call for of on an object, what is it looking for?
- First of all it looks for a [Symbol.iterator] key
- Then it looks for an object to be returned from that key. So [Symbol.iterator] has to be the key for a function that can return objects [Symbol.iterator] = function() {
- Then it looks for and calls the next() method on that object.
- Then it looks for an object to be return from that method with this format: { done: whether the iteration ought to stop, value: the value to return }
Why do we want to reduce nesting?
Reducing nesting results in more readable code, which leads to discoverable bugs, faster developer iteration, and increased stability.
When do you want to put Symbol.iterator on an object, ?
when you want to run an iteration method on it and return a series of values.
What happens with this code?
Why?
let myObject = { apple: 'one', [Symbol.iterator]() { return { end: 0, next() { if (this.end < 2) { this.end++; return {done: false, value: this.end} } return {done: true, value: this.end} } } } }
console.log(…myObject);
logs: 1, 2
spread syntax spreads the iterable values.
Access the input in this function aFunction('apple', 'pear', 'orange'); function aFunction() { //only change code in here {
aFunction('apple', 'pear', 'orange'); function aFunction() { argument[0], argument[1] argument[2] {
What is the argument variable?
a special array-like object named arguments that contains all arguments by their index
Better to just use rest operator
What is the downside to the ‘argument’ variable? (2 things)
- But the downside is that although arguments is both array-like and iterable, it’s not an array. It does not support array methods, so we can’t call arguments.map(…) for example.
- Also, it always contains all arguments. We can’t capture them partially, like we did with rest parameters.
So when we need these features, then rest parameters are preferred.
What happens when you use the arguments variable inside an arrow function?
If we access the arguments object from an arrow function, it takes them from the outer “normal” function.
function f() {
let showArg = () => alert(arguments[0]);
showArg();
}
f(1); // 1
What happens with this code?
let str = “Hello”;
alert( […str] );
// H,e,l,l,o
The spread syntax works on iterators
What is alerted? let obj = { a: 1, b: 2, c: 3 };
let objCopy = { …obj };
alert(JSON.stringify(obj) === JSON.stringify(objCopy));
alert(obj === objCopy);
obj.d = 4;
alert(JSON.stringify(obj));
alert(JSON.stringify(objCopy));
// true // false (not same reference) // {"a":1,"b":2,"c":3,"d":4} // {"a":1,"b":2,"c":3}