Codin

Ace your homework & exams now with Quizwiz!

O(ab)

1 void printUnorderedPairs(int[] arrayA, int[] arrayB) { 2 for (inti= 0; i < arrayA.length; i++) { 3 for (int j = 0; j < arrayB.length; j++) { 4 if (arrayA[i] < arrayB[j]) { 5 System.out.println(arrayA[i] + "," + arrayB[j]); 6 } 7 } 8 } 9 } We can break up this analysis. The if statement within j's for loop is 0( 1) time since it's just a sequence of constant-time statements. We now have this: 1 void printUnorderedPairs(int[] arrayA, int[] arrayB) { CrackingTheCodinglnterview.com I 6th Edition 47 VI I Big 0 2 for (int i= 0; i < arrayA.length; i++) { 3 for (int j = 0; j < arrayB.length; j++) { 4 /* 0(1) work*/ 5 } 6 } 7 } For each element of array A, the inner for loop goes through b iterations, where b = arrayB. length. lfa = arrayA.length,then the runtime isO(ab). If you said O( N 2), then remember your mistake for the future. It's not O(N2) because there are two different inputs. Both matter. This is an extremely common mistake.

Parsing returned array from a function with deconstruction

A function may return an array of values. The array destructuring make it more intuitive to parse the returned array from a function. Consider the following example. 1 2 3 4 5 6 7 8 9 function returnArray() { return [10, 20, 30]; } let [a, b, c] = returnArray(); console.log(a); // 10 console.log(b); // 20 console.log(c); // 30 In this example, the returnArray() function returns an array of three elements. We used the array destructuring to destructure the array elements into the individual variables a, b, and c in a single line of code. Of course, you can use the array destructuring to ignore some of the returned values. Suppose, you only interested in the first and the third elements returned from the returnArray() function, you can ignore the second element of the array as follows: 1 2 3 [a, , c] = returnArray(); console.log(a); // 10 console.log(c); // 30

returning a function

A function with an empty return or without it returns undefined If a function does not return a value, it is the same as if it returns undefined: function doNothing() { /* empty */ } alert( doNothing() === undefined ); // true An empty return is also the same as return undefined: function doNothing() { return; } alert( doNothing() === undefined );

property existence check

A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns undefined. It provides a very common way to test whether the property exists - to get it and compare vs undefined:

variable

A variable is an information storage area. Every variable has a name, a value and a type. In JavaScript, the type of a variable is deduced from the value stored in it: JavaScript is a dynamically typed language. A variable is declared using the let keyword followed by the variable name. To declare a constant (a variable whose initial value will never change), it's better to use the const keyword instead.

best worst and expect O

Best Case: If all elements are equal, then quick sort will, on average, just traverse through the array once. This is O ( N). (This actually depends slightly on the implementation of quick sort. There are implementations, though, that will run very quickly on a sorted array.) • Worst Case: What if we get really unlucky and the pivot is repeatedly the biggest element in the array? (Actually, this can easily happen. If the pivot is chosen to be the first element in the subarray and the array is sorted in reverse order, we'll have this situation.) In this case, our recursion doesn't divide the array in half and recurse on each half. It just shrinks the subarray by one element. This will degenerate to anO(N2) runtime. Expected Case: Usually, though, these wonderful or terrible situations won't happen. Sure, sometimes the pivot will be very low or very high, but it won't happen over and over again. We can expect a runtime ofO(N log N). We rarely ever discuss best case time complexity, because it's not a very useful concept. After all, we could take essentially any algorithm, special case some input, and then get an O ( 1) time in the best case.

Amorize Time

An Array List, or a dynamically resizing array, allows you to have the benefits of an array while offering flexibility in size. You won't run out of space in the Arraylist since its capacity will grow as you insert elements. An Arraylist is implemented with an array. When the array hits capacity, the Arraylist class will create a new array with double the capacity and copy all the elements over to the new array. How do you describe the runtime of insertion? This is a tricky question. The array could be full. If the array contains N elements, then inserting a new element will take O(N) time. You will have to create a new array of size 2N and then copy N elements over. This insertion will take O ( N) time. However, we also know that this doesn't happen very often. The vast majority of the time insertion will be inO(l) time. We need a concept that takes both into account. This is what amortized time does. It allows us to describe that, yes, this worst case happens every once in a while. But once it happens, it won't happen again for so long that the cost is "amortized:' In this case, what is the amortized time? As we insert elements, we double the capacity when the size of the array is a power of 2. So after X elements, we double the capacity at array sizes 1, 2, 4, 8, 16, ... , X. That doubling takes, respectively, 1, 2, 4, 8, 16, 32, 64, ... , X copies. What is the sum of 1 + 2 + 4 + 8 + 16 + ... + X? If you read this sum left to right, it starts with 1 and doubles until it gets to X. If you read right to left, it starts with X and halves until it gets to 1. What then is the sum of X + X + X + X + ... + 1 ?This is roughly 2X. Therefore, X insertions take 0( 2X) time. The amortized time for each insertion is 0( 1).

Fibinacci Sequence big o

An example of an O(2n) function is the recursive calculation of Fibonacci numbers. O(2n) denotes an algorithm whose growth doubles with each addition to the input data set. The growth curve of an O(2n) function is exponential - starting off very shallow, then rising meteorically.

expression

An expression is a piece of code that produces a value. An expression is created by combining variables, values and operators. Every expression has a value and thus a type. Calculating an expression's value is called evaluation. During evaluation, variables are replaced by their values.

skipping parts of for loops

Any part of for can be skipped. For example, we can omit begin if we don't need to do anything at the loop start. Like here: let i = 0; // we have i already declared and assigned for (; i < 3; i++) { // no need for "begin" alert( i ); // 0, 1, 2 } We can also remove the step part: let i = 0; for (; i < 3;) { alert( i++ ); }

When to choose Function Declaration versus Function Expression?

As a rule of thumb, when we need to declare a function, the first to consider is Function Declaration syntax. It gives more freedom in how to organize our code, because we can call such functions before they are declared. That's also better for readability, as it's easier to look up function f(...) {...} in the code than let f = function(...) {...}. Function Declarations are more "eye-catching". ...But if a Function Declaration does not suit us for some reason, or we need a conditional declaration (we've just seen an example), then Function Expression should be used.

regex character classes

Character classes allow you to define a group of characters you wish to match by placing them inside square ([ and ]) brackets. For example, you want to match "bag", "big", and "bug" but not "bog". You can create the regex /b[aiu]g/ to do this. The [aiu] is the character class that will only match the characters "a", "i", or "u". let bigStr = "big"; let bagStr = "bag"; let bugStr = "bug"; let bogStr = "bog"; let bgRegex = /b[aiu]g/; bigStr.match(bgRegex); // Returns ["big"]

Statement

Each instruction inside a program is called a statement. A statement in JavaScript usually ends with a semicolon (albeit it's not strictly mandatory).

regex -

For example, /[0-5]/ matches any number between 0 and 5, including the 0 and 5. Also, it is possible to combine a range of letters and numbers in a single character set. let jennyStr = "Jenny8675309"; let myRegex = /[a-z0-9]/ig; // matches all letters and numbers in jennyStr jennyStr.match(myRegex);

+ reg

For example, /a+/g would find one match in "abc" and return ["a"]. Because of the +, it would also find a single match in "aabc" and return ["aa"]. If it were instead checking the string "abab", it would find two matches and return ["a", "a"] because the a characters are not in a row - there is a b between them. Finally, since there is no "a" in the string "bcd", it wouldn't find a match.

naming functions

Functions are actions. So their name is usually a verb. It should be brief, as accurate as possible and describe what the function does, so that someone reading the code gets an indication of what the function does. It is a widespread practice to start a function with a verbal prefix which vaguely describes the action. There must be an agreement within the team on the meaning of the prefixes. For instance, functions that start with "show" usually show something. Function starting with... "get..." - return a value, "calc..." - calculate something, "create..." - create something, "check..." - check something and return a boolean, etc. Examples of such names: showMessage(..) // shows a message getAge(..) // returns the age (gets it somehow) calcSum(..) // calculates a sum and returns the result createForm(..) // creates a form (and usually returns it) checkPermission(..) // checks a permission, returns true/false

javascript optimization

Generational collection - objects are split into two sets: "new ones" and "old ones". Many objects appear, do their job and die fast, they can be cleaned up aggressively. Those that survive for long enough, become "old" and are examined less often. Incremental collection - if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the garbage collection into pieces. Then the pieces are executed one by one, separately. That requires some extra bookkeeping between them to track changes, but we have many tiny delays instead of a big one. Idle-time collection - the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution. There are other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And, what's even more important, things change as engines develop, so going deeper "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.

breaks in switch statements

If there is no break then the execution continues with the next case without any checks. An example without break: let a = 2 + 2; switch (a) { case 3: alert( 'Too small' ); case 4: alert( 'Exactly!' ); case 5: alert( 'Too big' ); default: alert( "I don't know such values" ); } In the example above we'll see sequential execution of three alerts: alert( 'Exactly!' ); alert( 'Too big' ); alert( "I don't know such values" );

function declaration scoping

In strict mode, when a Function Declaration is within a code block, it's visible everywhere inside that block. But not outside of it. For instance, let's imagine that we need to declare a function welcome() depending on the age variable that we get during runtime. And then we plan to use it some time later. If we use Function Declaration, it won't work as intended: let age = prompt("What is your age?", 18); // conditionally declare a function if (age < 18) { function welcome() { alert("Hello!"); } } else { function welcome() { alert("Greetings!"); } } // ...use it later welcome(); // Error: welcome is not defined What can we do to make welcome visible outside of if? The correct approach would be to use a Function Expression and assign welcome to the variable that is declared outside of if and has the proper visibility. This code works as intended: let age = prompt("What is your age?", 18); let welcome; if (age < 18) { welcome = function() { alert("Hello!"); }; } else { welcome = function() { alert("Greetings!"); }; } welcome(); // ok now

vertical indentation

Insert an extra newline where it helps to make the code more readable. There should not be more than nine lines of code without a vertical indentation.

A primitive

Is a value of a primitive type. There are 6 primitive types: string, number, boolean, symbol, null and undefined.

A object

Is capable of storing multiple values as properties. Can be created with {}, for instance: {name: "John", age: 30}. There are other kinds of objects in JavaScript: functions, for example, are objects.

template literal

It is possible to include expressions in a string by using backticks (`) to delimit the string. Such a string is called a template literal. Inside a template literal, expressions are identified by the ${expression} syntax.

drop constants

It is very possible for O(N) code to run faster than 0( 1) code for specific inputs. Big O just describes the rate of increase. For this reason, we drop the constants in runtime. An algorithm that one might have described as 0(2N) is actuallyO(N). Many people resist doing this. They will see code that has two (non-nested) for loops and continue this 0 ( 2N). They think they're being m If you're going to count the number of instructions, then you'd have to go to the assembly level and take into account that multiplication requires more instructions than addition, how the compiler would optimize something, and all sorts of other details. This would be horrendously complicated, so don't even start going down this road. Big O allows us to express how the runtime scales. We just need to accept that it doesn't mean that O(N) is always As you may recall, the base of a log doesn't matter for big O since logs of different bases are only different by a constant factor. However, this does not apply to exponents. The base of an exponent does matter. Compare 2" and 8". If you expand 8", you get (23)", which equals 2 3 ", which equals 2 2 " * 2".

Space Complexity

Its the amount of memory space required by the algorithm, during the course of its execution. Space complexity must be taken seriously for multi-user systems and in situations where limited memory is available. An algorithm generally requires space for following components : Instruction Space: Its the space required to store the executable version of the program. This space is fixed, but varies depending upon the number of lines of code in the program. Data Space: Its the space required to store all the constants and variables(including temporary variables) value. Environment Space: Its the space required to store the environment information needed to resume the suspended function. To learn about Space Complexity in detail, jump to the Space Complexity tutorial.

nan

JavaScript is extremely tolerant in terms of type conversion. However, sometimes conversion isn't possible. If a value fails to convert into a number, you'll get the result NaN (Not a Number). const g = "five" * 2; console.log(g); // Show NaN

mocha test

Let's say we want to make a function pow(x, n) that raises x to an integer power n. We assume that n≥0. That task is just an example: there's the ** operator in JavaScript that can do that, but here we concentrate on the development flow that can be applied to more complex tasks as well. Before creating the code of pow, we can imagine what the function should do and describe it. Such description is called a specification or, in short, a spec, and contains descriptions of use cases together with tests for them, like this: describe("pow", function() { it("raises to n-th power", function() { assert.equal(pow(2, 3), 8); }); });

positive lookahead

Lookaheads are patterns that tell JavaScript to look-ahead in your string to check for patterns further along. This can be useful when you want to search for multiple patterns over the same string. There are two kinds of lookaheads: positive lookahead and negative lookahead. A positive lookahead will look to make sure the element in the search pattern is there, but won't actually match it. A positive lookahead is used as (?=...) where the ... is the required part that is not matched. let quit = "qu"; let noquit = "qt"; let quRegex= /q(?=u)/; let qRegex = /q(?!u)/; quit.match(quRegex); // Returns ["q"]

interlock objects

Now a more complex example. The family: function marry(man, woman) { woman.husband = man; man.wife = woman; return { father: man, mother: woman } } let family = marry({ name: "John" }, { name: "Ann" }); Function marry "marries" two objects by giving them references to each other and returns a new object that contains them both. The resulting memory structure: As of now, all objects are reachable. Now let's remove two references: delete family.father; delete family.mother.husband; It's not enough to delete only one of these two references, because all objects would still be reachable. But if we delete both, then we can see that John has no incoming reference any more: Outgoing references do not matter. Only incoming ones can make an object reachable. So, John is now unreachable and will be removed from the memory with all its data that also became unaccessible

constant time

O(1)

As you may recall, the base of a log doesn't matter for big O since logs of different bases are only different by a constant factor. However, this does not apply to exponents. The base of an exponent does matter. Compare 2" and 8". If you expand 8", you get (23)", which equals 2 3 ", which equals 2 2 " * 2". As you can see, 8" and 2 ° are different by a factor of 2 2 ". That is very much not a constant factor! The space complexity of this algorithm will be O(N). Although we have 0(2N) nodes in the tree total, only O(N) exist at any given time. Therefore, we would only need to have O(N) memory available. � Examples and Exercises Big O time is a difficult concept at first. However, once it"clicks;' it gets fairly easy. The same patterns come up again and again, and the rest you can derive. We'll start off easy and get progressively more difficult. CrackingTheCodi

O(N) dominatesO(log N),so we can drop theO(log N). There is no established relationship between N and M, so we have to keep both variables in there. Therefore, all but the last one are equivalent to O(N). Examples Suppose we had an algorithm that took in an array of strings, sorted each string, and then sorted the full array. What would the runtime be? Many candidates will reason the following: sorting each string is O(N log N) and we have to do this for each string, so that's O(N*N log N). We also have to sort this array, so that's an additional O(N log N) work.Therefore,the total runtime isO(N2 log N + N log N),which isjust0(N2 log N). This is completely incorrect. Did you catch the error? The problem is that we used N in two different ways. In one case, it's the length of the string (which string?). And in another case, it's the length of the array. In your interviews, you can prevent this error by either not using the variable "N" at all, or by only using it when there is no ambiguity as to what N could represent. In fact, I wouldn't even use a and b here, or m and n. It's too easy to forget which is which and mix them up. An O(a2 ) runtime is completely different from an O(a*b) runtime. Let's define new terms-and use names that are logical. Let s be the length of the longest string. Let a be the length of the array. Now we can work through this in parts: • Sorting each string is 0( s log s). • We have to do this for every string (and there are a strings), so that's 0( a* s log s). Now we have to sort all the strings. There are a strings, so you'll may be inclined to say that this takes O ( a log a) time. This is what most candidates would say. You should also take into account that you need to compare the strings. Each string comparison takes O(s) time. There are O(a log a) comparisons, therefore this will take 0( a*s log a) time. If you add up these two parts, you get 0( a* s ( log a + log s)). This is it. There is no way to reduce it further. Example!) The following simple code sums the values of all the nodes in a balanced binary search tree. What is its runtime? 1 int sum(N

linear time

O(n)

quadratic time

O(n^2)

negative lookahead

On the other hand, a negative lookahead will look to make sure the element in the search pattern is not there. A negative lookahead is used as (?!...) where the ... is the pattern that you do not want to be there. The rest of the pattern is returned if the negative lookahead part is not present.

~ bitwise

One of the old tricks used here is the bitwise NOT ~ operator. It converts the number to a 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation. In practice, that means a simple thing: for 32-bit integers ~n equals -(n+1). For instance: alert( ~2 ); // -3, the same as -(2+1) alert( ~1 ); // -2, the same as -(1+1) alert( ~0 ); // -1, the same as -(0+1) alert( ~-1 ); // 0, the same as -(-1+1) As we can see, ~n is zero only if n == -1 (that's for any 32-bit signed integer n). So, the test if ( ~str.indexOf("...") ) is truthy only if the result of indexOf is not -1. In other words, when there is a match. People use it to shorten indexOf checks: let str = "Widget"; if (~str.indexOf("Widget")) { alert( 'Found it!' ); // works }

Math.trunc (not supported by Internet Explorer)

Removes anything after the decimal point without rounding: 3.1 becomes 3, -1.1 becomes -1.

.match

To use the .match() method, apply the method on a string and pass in the regex inside the parentheses. Here's an example: "Hello, World!".match(/Hello/); // Returns ["Hello"] let ourStr = "Regular expressions"; let ourRegex = /expressions/; ourStr.match(ourRegex); // Returns ["expressions"]

Math.floor

Rounds down: 3.1 becomes 3, and -1.1 becomes -2.

Math.round

Rounds to the nearest integer: 3.1 becomes 3, 3.6 becomes 4 and -1.1 becomes -1.

Math.ceil

Rounds up: 3.1 becomes 4, and -1.1 becomes -1.

Grouping switch "case"

Several variants of case which share the same code can be grouped. For example, if we want the same code to run for case 3 and case 5: let a = 2 + 2; switch (a) { case 4: alert('Right!'); break; case 3: // (*) grouped two cases case 5: alert('Wrong!'); alert("Why don't you take a math class?"); break; default: alert('The result is strange. Really.'); } Now both 3 and 5 show the same message.

object with square brackets

Square brackets also provide a way to obtain the property name as the result of any expression - as opposed to a literal string - like from a variable as follows: let key = "likes birds"; // same as user["likes birds"] = true; user[key] = true; Here, the variable key may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility. For instance: let user = { name: "John", age: 30 }; let key = prompt("What do you want to know about the user?", "name"); // access by variable alert( user[key] ); // John (if enter "name") The dot notation cannot be used in a similar way: let user = { name: "John", age: 30 }; let key = "name"; user.key // undefined

space vs time complexity

Stack space in recursive calls counts, too. For example, code like this would takeO(n) time and O(n) space. 1 int sum(int n) {/*Ex 1.*/ 2 if (n <= 0) { 3 return 0; 4 } 5 return n + sum(n-1); 6 } Each call adds a level to the stack. 1 sum(4) 2 -> sum(3) 3 -> sum(2) 4 -> sum(l) 5 -> sum(0) Each of these calls is added to the call stack and takes up actual memory. However, just because you have n calls total doesn't mean it takes O(n) space. Consider the below function, which adds adjacent elements between O and n: int pairSumSequence(int n) {/* Ex 2.*/ int sum = 0; for (inti= 0; i < n; i++) { sum+= pairSum(i, i + 1); } return sum; } int pairSum(int a, int b) { return a+ b; } There will be roughly O(n) calls to pairSum. However, those calls do not exist simultaneously on the call stack, so you only need O ( 1) space.

function declaration vs function expression

The syntax that we used before is called a Function Declaration: function sayHi() { alert( "Hello" ); } There is another syntax for creating a function that is called a Function Expression. It looks like this: let sayHi = function() { alert( "Hello" ); };

object deconstructing

Suppose you have an object person with two properties: firstName and lastName. 1 2 3 4 let person = { firstName: 'John', lastName: 'Doe' }; When you destructure an object, the variable names must match the property names in the object. If the variable name does not match, it receives a value of undefined. The following example assigns the firstName and lastName variables the values of the firstName and lastName property of the person object. 1 2 3 let {firstName, lastName} = person; console.log(firstName); // John console.log(lastName); // Doe In this example, we declare and assign the variables in the same statement. Also, you can use the object destructuring in the assignment statement, however, it must be surrounded by parentheses. See the following example. 1 ({firstName,lastName}) = person; If you don't use the parentheses, JavaScript engine interprets the left-hand side as a block. Hence, you would get an error. Nested object and object destructuring Assuming that you have an employee object which has a profile object as the property. 1 2 3 4 5 6 7 let employee = { id: 1001, profile: { firstName: 'John', lastName: 'Doe' } }; The following statement destructures the profile property into individual variables. 1 2 3 4 let {profile: {firstName, lastName}} = employee; console.log(firstName); // John console.log(lastName); // Doe In this tutorial, you have learned how to use ES6 destructuring feature to destructure an array or an object into individual variables.

\w

The closest character class in JavaScript to match the alphabet is \w. This shortcut is equal to [A-Za-z0-9_]. This character class matches upper and lowercase letters plus numbers. Note, this character class also includes the underscore character (_).

continue

The continue directive is a "lighter version" of break. It doesn't stop the whole loop. Instead, it stops the current iteration and forces the loop to start a new one (if the condition allows). We can use it if we're done with the current iteration and would like to move on to the next one. The loop below uses continue to output only odd values: for (let i = 0; i < 10; i++) { // if true, skip the remaining part of the body if (i % 2 == 0) continue; alert(i); // 1, then 3, 5, 7, 9 } Continue doesn't work with ternaries

str.indexOf

The first method is str.indexOf(substr, pos). It looks for the substr in str, starting from the given position pos, and returns the position where the match was found or -1 if nothing can be found. For instance: let str = 'Widget with id'; alert( str.indexOf('Widget') ); // 0, because 'Widget' is found at the beginning alert( str.indexOf('widget') ); // -1, not found, the search is case-sensitive alert( str.indexOf("id") ); // 1, "id" is found at the position 1 (..idg The optional second parameter allows us to search starting from the given position. For instance, the first occurrence of "id" is at position 1. To look for the next occurrence, let's start the search from position 2: let str = 'Widget with id'; alert( str.indexOf('id', 2) ) // 12 If we're interested in all occurrences, we can run indexOf in a loop. Every new call is made with the position after the previous match: let str = 'As sly as a fox, as strong as an ox'; let target = 'as'; // let's look for it let pos = 0; while (true) { let foundPos = str.indexOf(target, pos); if (foundPos == -1) break; alert( `Found at ${foundPos}` ); pos = foundPos + 1; // continue the search from the next posi

for..of

The for..of doesn't give access to the number of the current element, just its value, but in most cases that's enough. And it's shorter. Technically, because arrays are objects, it is also possible to use for..in: let arr = ["Apple", "Orange", "Pear"]; for (let key in arr) { alert( arr[key] ); // Apple, Orange, Pear } But that's actually a bad idea. There are potential problems with it: The loop for..in iterates over all properties, not only the numeric ones. There are so-called "array-like" objects in the browser and in other environments, that look like arrays. That is, they have length and indexes properties, but they may also have other non-numeric properties and methods, which we usually don't need. The for..in loop will list them though. So if we need to work with array-like objects, then these "extra" properties can become a problem. The for..in loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it's still very fast. The speedup may only matter in bottlenecks. But still we should be aware of the difference. Generally, we shouldn't use for..in for arrays.

memory reachability

The main concept of memory management in JavaScript is reachability. Simply put, "reachable" values are those that are accessible or usable somehow. They are guaranteed to be stored in memory. There's a base set of inherently reachable values, that cannot be deleted for obvious reasons. For instance: Local variables and parameters of the current function. Variables and parameters for other functions on the current chain of nested calls. Global variables. (there are some other, internal ones as well) These values are called roots. Any other value is considered reachable if it's reachable from a root by a reference or by a chain of references. For instance, if there's an object in a local variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.

concat

The method arr.concat creates a new array that includes values from other arrays and additional items. The syntax is: arr.concat(arg1, arg2...) It accepts any number of arguments - either arrays or values. The result is a new array containing items from arr, then arg1, arg2 etc. If an argument argN is an array, then all its elements are copied. Otherwise, the argument itself is copied. For instance: let arr = [1, 2]; // create an array from: arr and [3,4] alert( arr.concat([3, 4])); // 1,2,3,4 // create an array from: arr and [3,4] and [5,6] alert( arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6 // create an array from: arr and [3,4], then add values 5 and 6 alert( arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6 Normally, it only copies elements from arrays. Other objects, even if they look like arrays, added as a whole: let arr = [1, 2]; let arrayLike = { 0: "something", length: 1 }; alert( arr.concat(arrayLike) ); // 1,2,[object Object] //[1, 2, arrayLike]

slice

The method arr.slice is much simpler than similar-looking arr.splice. The syntax is: arr.slice(start, end) It returns a new array copying to it all items from index start to end (not including end). Both start and end can be negative, in that case position from array end is assumed. It's similar to a string method str.slice, but instead of substringss it makes subarrays. For instance: let arr = ["t", "e", "s", "t"]; alert( arr.slice(1, 3) ); // e,s (copy from 1 to 3) alert( arr.slice(-2) ); // s,t (copy from -2 till the end)

toString(base)cx

The method num.toString(base) returns a string representation of num in the numeral system with the given base. For example: let num = 255; alert( num.toString(16) ); // ff alert( num.toString(2) ); // 11111111 The base can vary from 2 to 36. By default it's 10. Common use cases for this are: base=16 is used for hex colors, character encodings etc, digits can be 0..9 or A..F. base=2 is mostly for debugging bitwise operations, digits can be 0 or 1. base=36 is the maximum, digits can be 0..9 or A..Z. The whole latin alphabet is used to represent a number. A funny, but useful case for 36 is when we need to turn a long numeric identifier into something shorter, for example to make a short url. Can simply represent it in the numeral system with base 36:

indexOf/lastIndexOf and includes

The methods arr.indexOf, arr.lastIndexOf and arr.includes have the same syntax and do essentially the same as their string counterparts, but operate on items instead of characters: arr.indexOf(item, from) - looks for item starting from index from, and returns the index where it was found, otherwise -1. arr.lastIndexOf(item, from) - same, but looks for from right to left. arr.includes(item, from) - looks for item starting from index from, returns true if found. For instance: let arr = [1, 0, false]; alert( arr.indexOf(0) ); // 1 alert( arr.indexOf(false) ); // 2 alert( arr.indexOf(null) ); // -1 alert( arr.includes(1) )

variables in functions

The outer variable is only used if there's no local one. If a same-named variable is declared inside the function then it shadows the outer one. For instance, in the code below the function uses the local userName. The outer one is ignored: let userName = 'John'; function showMessage() { let userName = "Bob"; // declare a local variable let message = 'Hello, ' + userName; // Bob alert(message); } // the function will create and use its own userName showMessage(); alert( userName ); // John, unchanged, the function did not access the outer variable

regex ?

The question mark makes the preceding token in the regular expression optional. colou?r matches both colour and color. The question mark is called a quantifier.

block scope

The scope of a variable is the part of the program where the variable is visible. Variables declared with let or const are block-scoped. A code block is a portion of a program delimited by a pair of opening and closing curly braces { ... }

regex .

The wildcard character . will match any one character. The wildcard is also called dot and period. You can use the wildcard character just like any other character in the regex. For example, if you wanted to match "hug", "huh", "hut", and "hum", you can use the regex /hu./ to match all four words.

substring

There are 3 methods in JavaScript to get a substring: substring, substr and slice. str.slice(start [, end]) Returns the part of the string from start to (but not including) end. For instance: let str = "stringify"; alert( str.slice(0, 5) ); // 'strin', the substring from 0 to 5 (not including 5) alert( str.slice(0, 1) ); // 's', from 0 to 1, but not including 1, so only character at 0 If there is no second argument, then slice goes till the end of the string: let str = "stringify"; alert( str.slice(2) ); // ringify, from the 2nd position till the end Negative values for start/end are also possible. They mean the position is counted from the string end: let str = "stringify"; // start at the 4th position from the right, end at the 1st from the right alert( str.slice(-4, -1) ); // gif str.substring(start [, end]) Returns the part of the string between start and end. This is almost the same as slice, but it allows start to be greater than end. For instance: let str = "stringify"; // these are same for substring alert( str.substring(2, 6) ); // "ring" alert( str.substring(6, 2) ); // "ring" // ...but not for slice: alert( str.slice(2, 6) ); // "ring" (the same) alert( str.slice(6, 2) ); // "" (an empty string) Negative arguments are (unlike slice) not supported, they are treated as 0. str.substr(start [, length]) Returns the part of the string from start, with the given length. In contrast with the previous methods, this one allows us to specify the length instead of the ending position: let str = "stringify"; alert( str.substr(2, 4) ); // ring, from the 2nd position get 4 characters The first argument may be negative, to count from the end: let str = "stringify"; alert( str.substr(-4, 2) ); // gi, from the 4th position get 2 characters Let's recap these methods to avoid any confusion: method selects... negatives slice(start, end) from start to end (not including end) allows negatives substring(start, end) between start and end negative values mean 0 substr(start, length) from start get length characters allows negative start Which one to choose? All of them can do the job. Formally, substr has a minor drawback: it is described not in the core JavaScript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere. Of the other two variants, slice is a little bit more flexible, it allows negative arguments and shorter to write. So, it's enough to remember solely slice of these three methods.

horizontal line indentation

There are two types of indents: Horizontal indents: 2 or 4 spaces. A horizontal indentation is made using either 2 or 4 spaces or the horizontal tab symbol (key Tab). Which one to choose is an old holy war. Spaces are more common nowadays. One advantage of spaces over tabs is that spaces allow more flexible configurations of indents than the tab symbol. For instance, we can align the arguments with the opening bracket, like this: show(parameters, aligned, // 5 spaces padding at the left one, after, another ) { // ... }

indexof 0

There is a slight inconvenience with indexOf in the if test. We can't put it in the if like this: let str = "Widget with id"; if (str.indexOf("Widget")) { alert("We found it"); // doesn't work! } The alert in the example above doesn't show because str.indexOf("Widget") returns 0 (meaning that it found the match at the starting position). Right, but if considers 0 to be false. So, we should actually check for -1, like this: let str = "Widget with id"; if (str.indexOf("Widget") != -1) { alert("We found it"); // works now!

str.lastIndexOf(substr, position)

There is also a similar method str.lastIndexOf(substr, position) that searches from the end of a string to its beginning. It would list the occurrences in the reverse order.

new Array()

There is one more syntax to create an array: let arr = new Array("Apple", "Pear", "etc"); It's rarely used, because square brackets [] are shorter. Also there's a tricky feature with it. If new Array is called with a single argument which is a number, then it creates an array without items, but with the given length. Let's see how one can shoot themself in the foot: let arr = new Array(2); // will it create an array of [2] ? alert( arr[0] ); // undefined! no elements. alert( arr.length ); // length 2 In the code above, new Array(number) has all elements undefined. To evade such surprises, we usually use square brackets, unless we really know what we're doing.

o rcursion

Therefore, there will be 2 ° + 21 + 22 + 23 + 24 + . • . + 2N (which is 2 N • 1 - 1) nodes. (See "Sum of Powers of 2" on page 630.) Try to remember this pattern. When you have a recursive function that makes multiple calls, the runtime will often (but not always) look like O( branchesdepth), where branches is the number of times each recursive call branches. In this case, this gives us O ( 2N).

NaN

They belong to the type number, but are not "normal" numbers, so there are special functions to check for them: isNaN(value) converts its argument to a number and then tests it for being NaN: alert( isNaN(NaN) ); // true alert( isNaN("str") ); // true But do we need this function? Can't we just use the comparison === NaN? Sorry, but the answer is no. The value NaN is unique in that it does not equal anything, including itself: alert( NaN === NaN ); // false

\d

This is equal to the character class [0-9], which looks for a single character of any number between zero and nine.

reg ^

This is used to look for things excluding what comes after /[^asd]g will look for matches that don't have a or s or d you used the caret character (^) inside a character set to create a negated character set in the form [^thingsThatWillNotBeMatched]. Outside of a character set, the caret is used to search for patterns at the beginning of strings. let firstString = "Ricky is first and can be found."; let firstRegex = /^Ricky/; firstRegex.test(firstString); // Returns true let notFirst = "You can't find Ricky now."; firstRegex.test(notFirst); // Returns false

Space complexity

Time is not the only thing that matters in an algorithm. We might also care about the amount of memoryor space-required by an algorithm. Space complexity is a parallel concept to time complexity. If we need to create an array of size n, this will require 0( n) space. If we need a two-dimensional array of size nxn, this will require O( n 2) space.

nest levels

Try to avoid nesting code too many levels deep. For example, in the loop, it's sometimes a good idea to use the continue directive to avoid extra nesting. For example, instead of adding a nested if conditional like this: for (let i = 0; i < 10; i++) { if (cond) { ... // <- one more nesting level } } We can write: for (let i = 0; i < 10; i++) { if (!cond) continue; ... // <- no extra nesting level }

regex length

Usernames have to be at least two characters long. {2,} A two-letter username can only use alphabet letter characters. ^[a-z] Usernames have to be at least two characters long. {2,} A two-letter username can only use alphabet letter characters. ^[a-z] /^[a-z]{2,}\d*$/i; Recall that you use the plus sign + to look for one or more characters and the asterisk * to look for zero or more characters. These are convenient but sometimes you want to match a certain range of patterns. You can specify the lower and upper number of patterns with quantity specifiers. Quantity specifiers are used with curly brackets ({ and }). You put two numbers between the curly brackets - for the lower and upper number of patterns. For example, to match only the letter a appearing between 3 and 5 times in the string "ah", your regex would be /a{3,5}h

iterate through a string

We can also iterate over characters using for..of: for (let char of "Hello") { alert(char); // H,e,l,l,o (char becomes "H", then "e", then "l" etc) }

`computed properties

We can use square brackets in an object literal. That's called computed properties. For instance: let fruit = prompt("Which fruit to buy?", "apple"); let bag = { [fruit]: 5, // the name of the property is taken from the variable fruit }; alert( bag.apple );

constant yet large number

What about this strange bit of code? 1 void printUnorderedPairs(int[] arrayA, int[] arrayB) { 2 for (int i= 0; i < arrayA.length; i++) { 3 for (int j = 0; j < arrayB.length; j++) { 4 for (int k = 0; k < 100000; k++) { 5 System.out.println(arrayA[i] + "," + arrayB[j]); 6 } 7 } 8 } 9 } Nothing has really changed here. 100,000 units of work is still constant, so the runtime is 0( ab).

\D

You can also search for non-digits using a similar shortcut that uses an uppercase D instead. The shortcut to look for non-digit characters is \D. This is equal to the character class [^0-9], which looks for a single character that is not a number between zero and ni

\W

You can search for the opposite of the \w with \W. Note, the opposite pattern uses a capital letter. This shortcut is the same as [^A-Za-z0-9_].

\s

You can search for whitespace using \s, which is a lowercase s. This pattern not only matches whitespace, but also carriage return, tab, form feed, and new line characters. You can think of it as similar to the character class [ \r\t\f\n\v].

$

You can search the end of strings using the dollar sign character $ at the end of the regex. let theEnding = "This is a never ending story"; let storyRegex = /story$/; storyRegex.test(theEnding); // Returns true let noEnding = "Sometimes a story will have to end"; storyRegex.test(noEnding); // Returns false

dynamically typed

You don't have to define a variable type explicitly in JavaScript. Its type is deduced from the value stored in the variable and may change while the program runs. That's why we say that JavaScript is a dynamically typed lang

64-bit format IEEE-754

a number is represented in 64-bit format IEEE-754, so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point (they are zero for integer numbers), and 1 bit is for the sign. If a number is too big, it would overflow the 64-bit storage, potentially giving an infinity: alert( 1e500 ); // Infinity What may be a little less obvious, but happens quite often, is the loss of precision. Consider this (falsy!) test: alert( 0.1 + 0.2 == 0.3 ); // false That's right, if we check whether the sum of 0.1 and 0.2 is 0.3, we get false. Strange! What is it then if not 0.3? alert( 0.1 + 0.2 ); // 0.30000000000000004 Ouch! There are more consequences than an incorrect comparison here. Imagine you're making an e-shopping site and the visitor puts $0.10 and $0.20 goods into their chart. The order total will be $0.30000000000000004. That would surprise anyone. But why does this happen? A number is stored in memory in its binary form, a sequence of bits - ones and zeroes. But fractions like 0.1, 0.2 that look simple in the decimal numeric system are actually unending fractions in their binary form. In other words, what is 0.1? It is one divided by ten 1/10, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: 1/3. It becomes an endless fraction 0.33333(3). So, division by powers 10 is guaranteed to work well in the decimal system, but division by 3 is not. For the same reason, in the binary numeral system, the division by powers of 2 is guaranteed to work, but 1/10 becomes an endless binary fraction. There's just no way to store exactly 0.1 or exactly 0.2 using the binary system, just like there is no way to store one-third as a decimal fraction. The numeric format IEEE-754 solves this by rounding to the nearest possible number. These rounding rules normally don't allow us to see that "tiny precision loss", but it exists. We can see this in action: alert( 0.1.toFixed(20) ); // 0.10000000000000000555 And when we sum two numbers, their "precision losses" add up. That's why 0.1 + 0.2 is not exactly 0.3. Not only JavaScript The same issue exists in many other programming languages. PHP, Java, C, Perl, Ruby give exactly the same result, because they are based on the same numeric format. Can we work around the problem? Sure, the most reliable method is to round the result with the help of a method toFixed(n): let sum = 0.1 + 0.2; alert( sum.toFixed(2) ); // 0.30 // Hello! I'm a self-increasing number! alert( 9999999999999999 ); // shows 10000000000000000 This suffers from the same issue: a loss of precision. There are 64 bits for the number, 52 of them can be used to store digits, but that's not enough. So the least significant digits disappear. JavaScript doesn't trigger an error in such events. It does its best to fit the number into the desired format, but unfortunately, this format is not big enough.

regex *

et soccerWord = "gooooooooal!"; let gPhrase = "gut feeling"; let oPhrase = "over the moon"; let goRegex = /go*/; soccerWord.match(goRegex); // Returns ["goooooooo"] gPhrase.match(goRegex); // Returns ["g"]

Two dots to call a method

alert( 123456..toString(36) ); // 2n9c Please note that two dots in 123456..toString(36) is not a typo. If we want to call a method directly on a number, like toString in the example above, then we need to place two dots .. after it. If we placed a single dot: 123456.toString(36), then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now goes the method. Also could write (123456).toString(36).

lazy regex

can use the ? character to change it to lazy matching. "titanic" matched against the adjusted regex of /t[a-z]*?i/ returns ["ti"].

how to delete object property

delete user.age;

capture groups

find repeat substrings. You put the regex of the pattern that will repeat in between the parentheses. To specify where that repeat string will appear, you use a backslash (\) and then a number. This number starts at 1 and increases with each additional capture group you use. An example would be \1 to match the first group. The example below matches any word that occurs twice separated by a space: let repeatStr = "regex regex"; let repeatRegex = /(\w+)\s\1/; repeatRegex.test(repeatStr); // Returns true repeatStr.match(repeatRegex); // Returns ["regex regex", "regex"]

for in

for (key in object) { // executes the body for each key among object properties } For instance, let's output all properties of user: let user = { name: "John", age: 30, isAdmin: true }; for (let key in user) { // keys alert( key ); // name, age, isAdmin // values for the keys alert( user[key] ); // John, 30, true } Note that all "for" constructs allow us to declare the looping variable inside the loop, like let key here. Also, we could use another variable name here instead of key. For instance, "for (let prop in obj)" is also widely used. The short answer is: "ordered in a special fashion": integer properties are sorted, others appear in creation order. The details follow.

function call

function sayHi() { alert( "Hello" ); } alert( sayHi ); // shows the function code Please note that the last line does not run the function, because there are no parentheses after sayHi. There are programming languages where any mention of a function name causes its execution, but JavaScript is not like that. In JavaScript, a function is a value, so we can deal with it as a value. The code above shows its string representation, which is the source code. Function Declaration: a function, declared as a separate statement, in the main code flow. // Function Declaration function sum(a, b) { return a + b; } Function Expression: a function, created inside an expression or inside another syntax construct. Here, the function is created at the right side of the "assignment expression" =: // Function Expression let sum = function(a, b) { return a + b; }; A Function Expression is created when the execution reaches it and is usable only from that moment. Once the execution flow passes to the right side of the assignment let sum = function... - here we go, the function is created and can be used (assigned, called, etc. ) from now on. Function Declarations are different. A Function Declaration can be called earlier than it is defined. For example, a global Function Declaration is visible in the whole script, no matter where it is. That's due to internal algorithms. When JavaScript prepares to run the script, it first looks for global Function Declarations in it and creates the functions. We can think of it as an "initialization stage". And after all Function Declarations are processed, the code is executed. So it has access to these functions. For example, this works: sayHi("John"); // Hello, John function sayHi(name) { alert( `Hello, ${name}` ); } The Function Declaration sayHi is created when JavaScript is preparing to start the script and is visible everywhere in it. ...If it were a Function Expression, then it wouldn't work: sayHi("John"); // error! let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` ); };

rounding

importance: 4 According to the documentation Math.round and toFixed both round to the nearest number: 0..4 lead down while 5..9 lead up. For instance: alert( 1.35.toFixed(1) ); // 1.4 In the similar example below, why is 6.35 rounded to 6.3, not 6.4? alert( 6.35.toFixed(1) ); // 6.3 How to round 6.35 the right way? solution Internally the decimal fraction 6.35 is an endless binary. As always in such cases, it is stored with a precision loss. Let's see: alert( 6.35.toFixed(20) ); // 6.34999999999999964473 The precision loss can cause both increase and decrease of a number. In this particular case the number becomes a tiny bit less, that's why it rounded down. And what's for 1.35? alert( 1.35.toFixed(20) ); // 1.35000000000000008882 Here the precision loss made the number a little bit greater, so it rounded up. How can we fix the problem with 6.35 if we want it to be rounded the right way? We should bring it closer to an integer prior to rounding: alert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000 Note that 63.5 has no precision loss at all. That's because the decimal part 0.5 is actually 1/2. Fractions divided by powers of 2 are exactly represented in the binary system, now we can round it: alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(rounded) -> 6.4

static type

languages, like C or Java, require variable types to always be defined. This is called static typin

.length

length is a property People with a background in some other languages sometimes mistype by calling str.length() instead of just str.length. That doesn't work. Please note that str.length is a numeric property, not a function. There is no need to add parenthesis after it.

regex .test

let testStr = "freeCodeCamp"; let testRegex = /Code/; testRegex.test(testStr); // Returns true

toFixed(n)

method toFixed(n) rounds the number to n digits after the point and returns a string representation of the result. let num = 12.34; alert( num.toFixed(1) ); // "12.3" This rounds up or down to the nearest value, similar to Math.round: let num = 12.36; alert( num.toFixed(1) ); // "12.4" Please note that result of toFixed is a string. If the decimal part is shorter than required, zeroes are appended to the end: let num = 12.34; alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits

properties

object can be created with figure brackets {...} with an optional list of properties. A property is a "key: value" pair, where key is a string (also called a "property name"), and value can be anything.

Property value shorthand

often use existing variables as values for property names. For instance: function makeUser(name, age) { return { name: name, age: age // ...other properties }; } let user = makeUser("John", 30); alert(user.name); // John In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there's a special property value shorthand to make it shorter. Instead of name:name we can just write name, like this: function makeUser(name, age) { return { name, // same as name: name age // same as age: age // ... }; } We can use both normal properties and shorthands in the same object: let user = { name, // same as name:name age: 30 };

greedy regex

regular expressions, a greedy match finds the longest possible part of a string that fits the regex pattern and returns it as a match. The alternative is called a lazy match, which finds the smallest possible part of the string that satisfies the regex pattern. You can apply the regex /t[a-z]*i/ to the string "titanic". This regex is basically a pattern that starts with t, ends with i, and has some letters in between. Regular expressions are by default greedy, so the match would return ["titani"]. It finds the largest sub-string possible to fit the pattern.

Why is it faster to work with the end of an array than with its beginning?

ruits.shift(); // take 1 element from the start It's not enough to take and remove the element with the number 0. Other elements need to be renumbered as well. The shift operation must do 3 things: Remove the element with the index 0. Move all elements to the left, renumber them from the index 1 to 0, from 2 to 1 and so on. Update the length property. The more elements in the array, the more time to move them, more in-memory operations. The similar thing happens with unshift: to add an element to the beginning of the array, we need first to move existing elements to the right, increasing their indexes. And what's with push/pop? They do not need to move anything. To extract an element from the end, the pop method cleans the index and shortens length.

regex replace

search and replace text in a string using .replace() on a string. The inputs for .replace() is first the regex pattern you want to search for. The second parameter is the string to replace the match or a function to do something. let wrongText = "The sky is silver."; let silverRegex = /silver/; wrongText.replace(silverRegex, "blue"); // Returns "The sky is blue." You can also access capture groups in the replacement string with dollar signs ($). "Code Camp".replace(/(\w+)\s(\w+)/, '$2 $1'); // Returns "Camp Code"

multiword property

they must be quoted "likes birds": true For multiword properties, the dot access doesn't work: // this would give a syntax error user.likes birds = true That's because the dot requires the key to be a valid variable identifier There's an alternative "square bracket notation" that works with any string: let user = {}; // set user["likes birds"] = true; // get alert(user["likes birds"])

regex |

used for or comparisons /yes|no|maybe/

regex /i

used to ignore case so /ignorecase/i. This regex can match the strings "ignorecase", "igNoreCase", and "IgnoreCase"

how to add object property

user.isAdmin = true;


Related study sets

Division with a one digit divisor

View Set

Chapter 6 (Anxiety Disorders) Pre/Post Chapter Quizzes

View Set

Chapter 9: Care Coordination and Continuity in Health Care Settings and the Community

View Set

Point-Slope Form of a Line: Assignment

View Set

HUN - Chapter 11 : The Fat-Soluble Vitamins: A, D, E, and K

View Set

Chapter 18 Mastering Microbiology

View Set