A few compilation of notes that will be updated with many more + different UI

JavaScript

Nice little summary I found later. Use to refresh topics, not to learn them:

100+ JavaScript Concepts you Need to Know - YouTube


  • .js file

To add a source attribute into the html file use <script> tag

Recommended adding it at the bottom of the scrypt tag so it's loaded when all your elements are created

<body> 
<p>Paragraph</p>
<p>Test</p>
   
<script src="file.js"> </script>
<body>

Statements

An instruction to the computer or browser to do something and when your statement is done, you put a semi-colon to end theline

let x = 5;
  • Code blocks are by curly brackets

Variables

Building blocks of javascript

Three types to declare a variable.

  • var used if you must support old browsers (used from 1995 to 2015) and are function scoped ()

  • const used when the value or type is not supposed to change.

  • let Used when you can't use const and when a variable is not defined. Let is block scoped {}

 var text = "something";
 let age = 50;
 let dog;
 const on = true;

Variables should not start with a capital unless they are a class, the usual writing style is CamelCase

Not a rule but might be useful, use const as the default unless you need to change the variable.


Types

There are 7 types of data

  • String - anytime you have some text.

  • Number - Numbers that do and don't have decimals.

  • Object - Everything in JavaScript is an object.

  • Null - Nothing

  • Undefined - Nothing

  • Symbol - Gives a unique identifier

Types - String

Serie of characters like "John Doe". Can use single or double quotes

You can set a backlash `` to escape taking something like JavaScript syntaxt, instead taking it as text

const name = "John Doe";
const name2 = 'John Doe';
   
// By using a single quote JavaScript will think you are finishing your
// string, to prevent this use the backlash
const name3 = 'She's having a party'; // Error
const name4 = 'She's having a party'; 

To use a string in multiple lines you can use Backticks `

const text = `printing this
   
will contain the "return" character each enter
   
which means new line
   
`;
   
const html = `
      <div>
          <h1> Hi </h1>
      </div>
  ` 
  // we can use this to insert a string into HTML

Concatenation when two or more strings are combined into one

Interpolation when you include a variable between strings

const name = "John Doe";

const greeting = "Hi, I am " + name + ". Nice to meet you!";

It's also possible to interpolate by setting the string using Backticks ` and to represent the variable or statement use ${}

const name = "John";
const hello = `My name is ${name}, hi`;

//or

const name = "John";
console.log(`Hi my name is ${name} nice to meet you`);

Types - Number

typeof is a keyword that will show you what kind of type is a variable

const something = 100;

typeof something; //will return "number" type

There's multiplication, division, addition and substraction in numbers

const a = 5;
const b = 10;


a + b; //returns 15
a * b; // 50
a - b; // 5
a / b; // 0.5

Helper methods

If you type "Math." you can find some tools to change numbers, for example Math.round(20.7) will round it to 21, Math.floor() will round to the bottom, Math.random() will give you a random number between 0-1

Example

const cakes = 2;
const kids = 10;
const cakeForEach = Math.floor(cakes / kids);

console.log(`Each kid gets ${cakeForEach} of a cake`)

NaN

NaN means Not a Number. Happens when try to do math with something that is not a number, for example 10 / "dog"

typeof NaN; //returns "number"

Types - Objects

Small explanation, everything in JavaScript is an object

Used of collections of data and collections of functionality

Used to group things together, you can initialize an object by using curly brackets and a semi-colon at the end, inside you can store properties and values separated by a comma

const person = {
    first: "John",
    last: "Doe",
    age: 40
};

To access the properties you can use a dot

person.first //returns "John"
person.last //returns "Doe"
person.age //returns 40

Types - Null and undefined

There's two way to express nothing in JavaScript

  • Undefined

If you create a variable called test that has nothing in it, and try to print it console.log(test); you will get an undefined because the variable exist but does contains nothing.

let test;
console.log(test);
// returns "Undefined"
  • Null

While undefined is a variable that has not yet have a value set to it, null is nothing, you set the value to have nothing in it

let somethingUndefined; //has an undefined value
const somethingNull = null; 

Types - Booleans

Boolean are true or false, like a light switch, it's on or it's off

let isOn = false;

You can get a true or false by comparing values, one equal sign is to add (1+1), but to compare you should use 3 equals signs (1 === 1), and returns a boolean value because you are asking if 1 is equal to 1

let age = 20;
const canYouDrink = age > 21;
console.log(canYouDrink); //returns false, since age is less than 21
let age = 21;

const isTrue = age === 21; //returns true since age has a value of 21

Difference between two equals (1==1) and three equals (1===1)

Two equals check if the values match, but not the type

let age = 20;


console.log(age == "20");
//returns true, even while we are comparing a number to a string

But three equal also check for the type

let age = 20;
console.log(age === "20");
//returns false, because age contains a number, 20, and we are asking
// if it is equal to a String that contains 20

Functions

  • Allows us to set a group together a set of statements

  • Can take in data known as arguments

  • Regular functions declared are "hoisted" at the top of the file, that means if you try to call the variable "before" defining it, it will work since it's hoisted up

There are some functions already built in, such as

parseFloat("1.123") //returns 1.123 as a float number
toString() //returns true or false
Date.now() //Throws how many miliseconds since 1970 

Functions are created and defined and you can run it

To define a function you place parenthesis and you open a function block with curly braces

function calculate() {
    console.log("Running");
}

//function call/run
calculate();
function calculateTax() {
    const total = 100 * 1.13;
    console.log(total);
}

Scope

When you declare a variable inside a function, there's something called scope tha does not allow us to use that variable outside of the function

function test() {
    const hello = "This is a variable inside a function";
}

//This will create an error, since hello only exists inside the function
// block
console.log(hello);

To be able to use it outside the function, we would need to return the value and assign it to a variable

function test() {
    const hello = "This is a variable inside a function"; //temporary variable
    return hello;
}

const testFunction = test();
console.log(testFunction); //prints "This is a variable inside a function"

You can also work with the function, since it's return value is the one we declared before

function payment() {
    const hello = 10; //temporary variable
    return hello;
}

const myTotal = payment();
console.log(`Your total is ${payment()}, thank you`)
// prints "Your total is 10, thank you"

Parameters and Arguments

When you use variables inside the function that are declared outside the function, you need to pass data

Inisde the parenthesis you place the expected data as placeholder

function calculateBill (billAmount, taxRate) {
    const total = billAmount * 1 + taxRate;
    return total;
}

const myTotal = calculateBill(100, 0.13);

First you define some placeholder, so when you call your function, you give the actual values as arguments that you want it to use, this way the function is reusable and you can give the values you want when it's called.

You can also pass a variable as an argument when calling the function, but that variable will be recognized inside the function as only the value, not the variable's name.

function calculateBill (billAmount, taxRate) {
    const total = billAmount * 1 + taxRate;
    console.log(total);
    return total;
}

const meal = 100;
const tax = 0.3;
const myTotal = calculateBill(meal, tax);

In this example we are passing meal which has a value of 100, JavaScript will recognize inside the calculateBill function, that you pased a value of 100 and will asign it into the first placeholder you had, this means billAmount will have a value of 100, but we will not be able to use the meal variable inside, only it's value.

Default

You can set a default value for the parameters in case there is no value passed in. This will print in the console "Hi John doe, how are you?", but if we instead pass a string as an argument like "Fer", the console will say "Hi Fer, how are you?"

function test(name = "John Doe") {
    console.log(`Hi ${name}, how are you?`);
    return `Hi {name}, how are you?`;
}

test();

Passing functions

It is possible to pass a function to another function's paremeters

function ageCalc(age) {
    return age + 10;

}
//the name of the placeholder is not important for this
//it will never collide with the one above because of the scope
function ageGreet(age) {
    return `Your age in 10 years will be ${age}, congrats`;
}

ageGreet(ageCalc(10));

This will first call the function ageGreet, but in order to get the value the program will run ageCalc with a value of 10. Returning a 20, once it has that value it will run ageGreet and will print "You are in 10 years will be 20, congrats"


Ways to declare functions

  • Anonymous function

Functions without a name, must be used inside function expressions, inside a const

function (age) {
    return age + 10;
} 
//gives an error at first
  • Function expression

A function stored in a variable (goes with a semicolon since it's a variable declaration).

This function is not hoisted up since it's a variable

const ageGreet = function (age) {
    return age + 10;
};
  • Arrow function

  • A function with concise syntax

  • Are anonymous functions

  • Uses a "fat arrow" => to represent that it is a function

//Normal function
function inchToCM (inches) {
    return inches * 2.54;
}
// Converted into anonymous, changed function and placed fat arrow
// at the right of the parenthesis
const inchToCM = (inches) => {
    return inches * 2.54;
};
//Remove the function block and remove return
const inchToCM = (inches) => inches * 2.54;
//another example with the same ageCalc function we had above
const ageCalc = (age) => age + 10;

IIFE

Immediately Invoked Function Expression

It's possible to invoque a function immediatly if we make it anonymous and use parenthesis around it

(function () {
  console.log("Hi");
})();

Methods

A function inside an object

For example, console.log() Console is an object, log is a function

You can also skip writing "function()" by just adding parenthesis to the object propertie

const name = {
    name: "John",
    sayHi: function(){
        console.log("Hello friend");
 }
}

//Short hand method
const yell = {
    name: "John",
    yellHi() {
        console.log("HELLO FRIEND");
    }
}

In order to use this we would need to first call the object, by typing for example in our console:

name.sayHi();


yell.yellHi();

Callback functions

A function that'll happen when something is done, for example when someone clicks something runs a function.

Here's an example by creating a button in our HTML file, we give it a class called "click", save the click as a variable and add an event listener so it runs a function everytime it's clicked

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <button class="click">click me</button> 

    <script src="types.js"></script>
</body>
</html>
const name = {
    names: "John",
    sayHi: function(){
        console.log("Hello friend");
    }, 
    //Short hand method
    yellHi() {
        console.log("HELLO FRIEND");
    }
}


const button = document.querySelector(".click");
button.addEventListener("click", name.yellHi);

Timer callback

Gives a delay

setTimeout(function() {
console.log("done");
}, 1000)

Debugging

Ways to fix a problem, mostly in Dev Tools (Inspect element on web)

Console methods

  • console.log() - Prints something in the console

  • console.error() - The same but shows like an error in the console

  • console.warn() - Same but with warning

  • console.table() - When you have a list of objects shows a table with that info

  • console.count() - Counts how many times a function or variable has been called

  • console.group() - Creates a collapsable menu in the console that groups everything inside a function, it needs to be closed with console.groupEnd() and have the same string than the opening

function doALotOfStuff (abc) {
  console.group("Doing stuff");
  console.log("Hey");
  let conversion = abc = 10;
  console.error(conversion);
  console.warn("Be careful");
  console.groupEnd("Doing stuff");
}

Callstack or stack trace

When you get an error, it's important to read what it says, since you will be guide into where the error is and what is the trace into that error, for example if you are trying to run a function that calls a function that has an error, you'll get something like this:

"Uncaught ReferenceError: doesntExist is not defined

`at greet (debugging.js:47)`

`at go (debugging.js:52)`

`at bootstrap (debugging.js:58)`

`at debugging.js:61"`

Reading from top to botton, you can see that doesnExist is not defined, you can find it in the function greet inside the file called debugging.js in the line 47, the next line says that greet is called inside the function go, which is in the debugging.js file in the line 52. That is important in order to trace where an error is

Grabbing elements

You can to the Dev Tools in a web (inspect element) and see how those were created

If you go to the console and type $0 you'll see what you have selected in the elements tab, it's possible to get a value by typing $0.value

$("p") - will show the first paragraph

$$("p") - will show all of the paragraph elements

debugger;

If you place a debugger statement the program will pause at were the debugger was placed to inspect each lines to find an error

function test(grade) {
    console.log(`Your grade is: ${grade}, congrats`);
    debugger;
    console.log(`Hello`);
}

test("S+")

Network requests

Open Dev Tools and go to network tab to see how it's timed when you use functions to request, or API's

Break on Attribute

In Dev Tools you can right click an attribute, if you select Break on, it'll show how a modification is done


Scope

Answers where are the variables available to use

Var variables are attached to the window object and they are function scoped

  • Global variables

Are available anywhere in the application, they are outside functions, or module, or statement, etc.

  • Function and block scope

When variables are created inside of a function they can't be used outside

When you have curly brackets the variables inside are scoped in there

const age = 100;

function go() {
    const hair = "blonde;"
}
console.log(age);
console.log(hair); // Will throw error since hair is a variable 
                   // that's inside go function
if (1===1) {
  let age = 1;
}
console.log(age); //also throws an error


if (2===2) {
   var age = 2;
}
console.log(age) //this one will work since var is function scoped
  • Scope look out

If a variable is not found inside a Scope, it will go out one "block" and look for it, if it's not it'll go one up again

const age = 100;


function person () {
    const hair = "blonde";
    console.log(age);   
    console.log(hair);
}

age is not inside the function scope, but it will look for it going up one more level


Hoisting

  • Function declaration

Regular functions declared are "hoisted" at the top of the file, that means if you try to call the variable "before" defining it, it will work since it's hoisted up

  • Variable hoisting

There's also variable hoisting, but it will not hoist the value

console.log(age);
var age = 10;

Will log "undefined" since it moved up "var age;" but not the assignment of the value


Closures

When a function is done, the scoped variables inside of it that are NOT returned are not available to use.

But using closures it's possible to use that variables from outside the function. JavaScript is able to create a function inside a function that can still reach the parent scope

function outer() {
    const outerVar = "I'm an outer variable";
    function inner() {
        const innerVar = "I'm an inner variable";
        console.log(innerVar);
        console.log(outerVar); // Will print outerVar when running -
    }  // innerFunction, even when it's not in this scope and ran before
    return inner;
}

const innerFunction = outer(); // We returned inner() so it's actually inner
innerFunction();

When creating a variable like innerFunction , the function will run to give that variable something.

We can still access the outerVar variable by running the child function that includes a variable on the parent function

Closure creating a function

When you have functions inside of functions and you are able to access the closure inside of that.

What's happening here is that when you create the const sayHello you are giving the first function the argument of "hello", then returns the inner function. When you call the const sayHello and you give it an argument, the inner function is the one that will take that and store it into "name", then uses the myGreet const that was declared before.

function createGreeting (greeting = "") {
    const myGreet = greeting.toUpperCase();
    return function(name) {
     return `${myGreet} ${name}`;
    }
}

const sayHello = createGreeting("hello");
const sayHey = createGreeting("hey");
console.log(sayHello("Sam"));
console.log(sayHey("Kath"));

Closures to create a private variable

Each time hockeyGame or soccerGame are used, the score will increment by 1

But each one will have a different counter, since we created two win functions by using the createGame function two times, they each have their own private variable score. It's not accessible

function createGame (gameName) {
    let score = 0;
     return function win() {
        score ++;
        return `Your game ${gameName} score is ${score}`;
    }
}

const hockeyGame = createGame("Hockey");
const soccerGame = createGame("Soccer");

The DOM

When you write HTML and you view it on the browser, the browser turns into something called the Document Object Model (DOM).

Window

Is an object and is everything about the currently open window, there are some useful commands like window.location, innerWidth

Document is responsible for everything from the opening <html> tag to the closing </html>

The entire document is accessible to us with the document keyword

Navigator

It's a higher level object than the window since gives more information not only for the browser but the devide itself, things like webcam, audio access, battery level, gps coordenates

document.querySelector()

Before you can go with the items you need to first select them and access to a specific element on the page and once we have it we can now add content to it, add listeners to it

There's two main ways to work with selecting elements usually used after document.

  • querySelector

Will always give the **first** matching element
  • const p = document.querySelector("p");
    console.log(p);
    
  • querySelectorAll

    Gives a node list

    const d = document.querySelectorAll("div");
    console.log(d);
    

Both take one argument and it's almost the same selector from CSS

It's possible to get a parent child selectors

const img = document.querySelecto(".item img");

Will print a nodelist of the images inside an item class

const img = document.querySelector(".item img");

Will print the first image inside an item class

You can also do it like this

const item2 = document.querySelector(".item2");
const item2Image = item2.querySelector("img");
console.log(item2Image);

You can run querySelector off of that item so it only search in a limited scope.

document.querySelector will search in all the page and querySelector as a method of another element will only look inside that element

document.getElementById()

It's also possible to use document.getElementById() to do the same but using an id instead of a class in the html file but seems to be old and replaced by querySelector

document.getElementById("p");

Element properties and methods

HTML elements are objects with a lot of properties and we can access to those properties and change them, this way it's possible to change a text of an element

(You can use console.dir() to get the properties of an object)

Let's say we are gonna work with our h2 element

<div class="item">
            <img src="https://picsum.photos/200">
            <h2>Sub Div </h2>
            <p>Hi I'm an item</p>
        </div>
const header = document.querySelector("h2");
console.log(header.textContent); //prints "Sub Div"

header.textContent = "changed text";
console.log(header.textContent); //prints "changed text" since h2 content changed
  • textContent gives the text but also the style

  • innerText only gives the text

  • innterHTML gives only the elements inside another element

console.log(header.innerHTML);
Sub div
  • outerHTML gives the elements but also the one we selected
console.log(header.outerHTML);

<h2>
     Sub div
    </h2>

One way to add something to a text

<p class="pizza">Our pizza has tomato, pepperoni</p>
const pizzaIngredients = document.querySelector(".pizza");
pizzaIngredients.innerText = `${pizzaIngredients.innerText} and cheese`;

This can be a bit slow in big projects since reloads the whole line

Insert Adjacent Text

It's a method to add adjacent text to an element. Takes two arguments

element.insertAdjacentText(position, element);

  • beforebegin before the element itself (above the <p> element for example)

  • afterbegin inside the element before it's first child

  • beforeeend inside the element after the last child

  • afterend after the element itself

<p class="pizza">Our pizza has tomato, pepperoni</p>
const pizzaIngredients = document.querySelector(".pizza");
pizzaIngredients.insertAdjacentText("beforeend", ` and cheese`);

Changing classes

It's possible to add and remove classes with:

element.classList.toggle(nameOfTheClass);

  • classList.add();

  • classList.remove();

  • classList.toggle();

  • classList.contains();

<body>
    <img class="bigImg" src="https://picsum.photos/600"/>
</body>
const bigPic = document.querySelector(".bigImg");
console.log(bigPic.classList);

This will first print a DOMTokenList where we can see all the classes of the element

There's something called "Proto" that will show many methods that you can call against the element we have

const bigPic = document.querySelector(".bigImg");
bigPic.classList.add("amazing");

This will add the "amazing" class to the currently selected element, and we can work with that class in CSS, this way we can create an image that changes it's form to round whenever we want

.amazing {
    border-radius: 50%;
}

Transition

We can create a small transition adding and removing classes (HTML, CSS, JavaScript)

<body>
    <img class="bigImg" src="https://picsum.photos/600"/>
</body>
.bigImg {
    transition: all 2s;
}

.round{
    border-radius: 50%;
}
const bigPic = document.querySelector(".bigImg");
bigPic.classList.toggle("round");

Everytime we use this method in the console it will change to round or not round with an animation.

But we can add an event listener for it to do it each time you hover on it

const bigPic = document.querySelector(".bigImg");

function toggleRound () {
    bigPic.classList.toggle("round");
}

bigPic.addEventListener("mouseover", toggleRound);
bigPic.addEventListener("mouseout", toggleRound);

This will have the image round by default, when your mouse moves over the image it will loose the round class and change it's form, then whe you move out it changes into round again


Build in and custom data attributes

Getters and setters

You can access all the attributes that are on something and give them values to those attributes with "setters", you can also get them with "getters".

Setters will give this attributes to the element we had selected

const bigPic = document.querySelector(".bigImg");

bigPic.alt = "Cute cat"; //setter
bigPic.width = 200; // setter

console.log(bigPic.alt); //getter
  • getAttribute

Gives you the attribute of an element

const bigPic = document.querySelector(".bigImg");

const something = bigPic.getAttribute("alt"); // asking to get the alt attribute
console.log(something); //logs the alt attribute of bigPic
  • setAttribute

Replace or add an attribute to an element, the difference between this method and using just declarations like above is that if you need to work with non-standard attributes you will have to use .setAttribute method (standard attributes are the usual alt, width, title, src, etc. Non standard can be the ones you create)

const bigPic = document.querySelector(".bigImg");

bigPic.setAttribute("alt", "Bigger with transition"); 

Creating meta-data

You can attach metadata attributes by writing "data-" before, e.g. "data-name", "data-description", in order to access this data you would need to create a variable and access with .dataset

<p class="toy" data-item="First Toy">Hi I'm an item</p>
const toy1 =  document.querySelector(".toy");
console.log(toy1.dataset);

This will create a DOMStringMap with a list of the data with "item"

Load event in addEventListener

The getter can load before the image is downloaded, to solve this there's an eventListener that uses "load" for it to wait until all HTML, CSS and JavaScript are loaded to do whatever you want it to do

bigPic.addEventListener("load", function() {
    console.log(pic.naturalWidth);
});

Creating HTML (.createElement)

element.createElement()

To create an element in JavaScript or during a process or event, we use .createElement() method and write what element we want, this will create the element but not the content or another elements on it, so we need to do it too

const myParagraph = document.createElement("p");
myParagraph.classList.add("Paragraph");
myParagraph.textContent = "Paragraph text";

const myImg = document.createElement("img");
myImg.src = "https://picsum.photos/600";
myImg.alt = "cute sky";
myImg.width = 400;

const myDiv = document.createElement("div");
myDiv.classList.add("Container");

In order to place this where we want, we need to use an API by using something called appendChild();

appendChild

element.appendChild()

To place an element as a child of another element we use appendChild

  • The element will contain the variable that has the class of the element we want

  • The inserted child will go in the parameter, inside the parenthesis

const divBelow = document.createElement("div"); // Creating element
divBelow.classList.add("below"); // Giving class attribute to that div

const textBelow = document.createElement("p"); // creating element
textBelow.classList.add("paragraph"); //Giving class attribute to paragraph
textBelow.textContent = "I like cats"; // Giving text to paragraph

divBelow.appendChild(textBelow); // Inserting the paragraph inside div

const firstFigure = document.querySelector(".second-div");
// Creating variable that contains the selected area where we'll place

firstFigure.appendChild(divBelow); 
// Placing the div into the HTML which has the paragraph inside

Recommendation

The best way to do this is by adding the elements that are going to go inside the div into the div, then AFTER that, adding the div into the html later

Because if you add the div first, it will reload each team to add every paragraph you have inside that div. Like this example:

document.body.appendChild(myDiv); //adds created div into the body
myDiv.appendChild(myParagraph); //adds paragraph into the div
myDiv.appendChild(myImage); //adds image into the div

What this will do is that first it will create an empty space in the html, then it will go again and add the paragraph to it, then it will go again and add the image to it. Causing a reflow 3 times

Instead doing it in this other order

myDiv.appendChild(myParagraph); //adds paragraph into the div
myDiv.appendChild(myImage); //adds image into the div

document.body.appendChild(myDiv); //adds created div into the body

Will first add those paragraphs and images into the div and then display the div with all together, so it's a more efficient way causing a reflow 1 time only

insertAdjacentElement

element.insertAdjacentElement(position, element);

Could be also interpreted as: "in relation of this first element... place the second element in this position"

  • beforebegin before the element itself

  • afterbegin inside the element before it's first child

  • beforeeend inside the element after the last child

  • afterend after the element itself

We can create a h2 element that contains something and we can place it wherever we want like in this example

const myDiv = document.createElement("div");
myDiv.classList.add("nice");

const myParagraph = document.createElement("p");
myParagraph.textContent = "hello this is a paragraph";

const heading = document.createElement("h2");
heading.textContent = "text sample";

myDiv.appendChild(myParagraph);
document.body.appendChild(myDiv);

myDiv.insertAdjacentElement("beforebegin", heading);

Doing this you are placing a <h2> element that contains "text sample" before the <div class="nice">, so you are inserting a whole element above the other element that you just created

Whole example with all three ways to do it

const divBelow = document.createElement("div");
divBelow.classList.add("below");

const textBelow = document.createElement("p");
textBelow.classList.add("paragraph");
textBelow.textContent = "I like cats and pizza";
divBelow.appendChild(textBelow);

const firstFigure = document.querySelector(".second-div");
firstFigure.appendChild(divBelow);

const catImg = document.createElement("img");

catImg.alt = "Cute cat";
catImg.src = "https://t4.ftcdn.net/jpg/00/97/58/97/360_F_97589769_t45CqXyzjz0KXwoBZT9PRaWGHRk5hQqQ.jpg";
catImg.width = 100;

// pizza

const listContainer = document.createElement("ul");
listContainer.classList.add("list-container");

const item1 = document.createElement("li");
item1.textContent = "Cheese";

const item2 = document.createElement("li");
item2.textContent = "Tomato";

const item3 = document.createElement("li");
item3.textContent = "Pepperoni";

const item4 = document.createElement("li");
item4.textContent = "Not Onion";

const item5 = document.createElement("li");
item5.textContent = "Bread";

listContainer.appendChild(item1);
// listContainer.appendChild(item2);
listContainer.appendChild(item3);
// listContainer.appendChild(item4);
listContainer.appendChild(item5);

item3.insertAdjacentElement("beforebegin", item2);
item3.insertAdjacentElement("beforeend", item4);
listContainer.insertAdjacentElement("beforeend", catImg);


const clickButton = document.querySelector(".click");

// const showList = () => divBelow.appendChild(listContainer);
function showList () {
    divBelow.appendChild(listContainer);

}

clickButton.addEventListener("click", showList);

HTML from strings and XSS

.innerHTML

You can use .innerHTML as a getter and as a setter (Needs to be used with backticks ` )

const something = document.querySelector(".first-div");
console.log(something.innerHTML); // Prints the content of the div

And to change the HTML elements inside you can use it as a setter, can be uses instead of the createElement() method (it does not need to be indented)

const something = document.querySelector(".first-div");

something.innerHTML = `
<div> 
    <h1> Title </h1>
    <p> something </p>
</div>
`;

Adding HTML from strings does not count as an html element, this means you can't add attributes to this since it's recognized as a string, not as HTML elements

You can also add "${}" to this strings

const something = document.querySelector(".first-div");

const src = "https://picsum.photos/200"
const alt = "Random"

const newHTML = `
<div> 
    <h1> Title </h1>
    <img src="${src}" alt="${alt}"/>
</div>
`;


something.innerHTML = newHTML;

document.createRange().createContextualFragment()

Turns a string into a DOM element in order to add attributes to an added HTML from a string

const fragment = document.createRange().createContextualFragment(newHTML);

const change = newHTML.querySelector("img");
change.alt = "Cat";
change.remove();

XSS

Cross-site scripting

It's possible that by using the methods above, you let users be able to inject code and write elements with the strings they can write like usernames, they can write <script> tags into it to do whatever they want like draining someone's bank account.

Must check the way to fix this in security video


Traversing or removing nodes

Traversing means going up, down, over, you often need to select an element based on their position like grabing the parent element of a button

Difference between node and element

  • node

Nodes are in the DOM aka Document Object model. In the DOM, all parts of the document, such as elements, attributes, text, etc. are organized in a hierarchical tree-like structure; consisting of parents and children. These individual parts of the document are known as nodes. https://i.stack.imgur.com/ocR0a.png

The topmost node is the root node (Document Node) of the DOM tree, which has one child. HTML attributes such as id, class, title, style, etc. are also considered as nodes in DOM hierarchy.

Different properties to work with elements

console.log(something.children);

console.log(something.firstElementChild);

console.log(something.lastElementChild);

console.log(something.previousElementSibling);

console.log(something.nextElementSibling);

console.log(something.parentElement);
  • .children - shows the childs of an element

  • .firstElementChild -

  • .lastElementChild -

  • .previousElementSibling - gives you the item that's before

  • .nextElementSibling - next item besides it

  • .parentElement - Gives you the parent, can be used multiple times to keep going up

Removing nodes

Method .remove() will remove an element, but that will be kept inside the memory, so it's still possible to print it even after it was removed:

const text = document.createElement("h1");
text.textContent = "I like pizza";

const test = document.querySelector(".test");
test.appendChild(text);

text.remove();

console.log(text.innerHTML);

Events

element.addEventListener("event", callbackFunction);

When an element that's on the page is clicked, it's hovered, when they're dragged they emit events When this happens we can use an EventListener to listen this and react to them

Takes two arguments, three steps for events:

First get the element in order to listen it

const active = document.querySelector("event-button");

The listen the event

active.addEventListener("click", /*function*/ );

And then do something with it adding a function

active.addEventListener("click", function() {
    console.log("clicked");
});

The second argument for the event listener is a function, that we can create previously

const button = document.querySelector(".event-button"); //get event element

// function handleActive() {
//   console.log("clicked");
// }
const handleActive = () => console.log("clicked"); //callback function

button.addEventListener("click", handleActive); //listen and use the function

Not a rule but you can write handleSomething as the name of a function that will get passed in to an event handler

Binding means a function that's bound to an element

const button = document.querySelector(".event-button");

const handleActive = () => console.log("clicked");

button.addEventListener("click", handleActive);

button.removeEventListener("click", handleActive);
//will stop listening to the click event on this button

It's possible to have our own events, but here are the common events:

Listen on multiple items

If you have multiple elements selected, you will not be able to addEventListener them

// Wrong way    
const buyButtons = document.querySelectorAll("button.buy");

buyButtons.addEventListener("click", function() {
console.log("clicked");
}); //error since we used querySelectorAll

forEach

Is a method that will run a function for each item in the node list of the selected element

const allBuyButtons = document.querySelectorAll("button.buy");

const clicked = () => console.log("hello");

allBuyButtons.forEach(function(button) {
  button.addEventListener("click", clicked);
})

Note that we use "placeholder" to reference what the navigator is giving us

We get an event listener run for each, so all buttons with the class buy will do the same

const buyButtons = document.querySelectorAll("button.buy");

buyButtons.forEach(something => {
  something.addEventListener("click", () => {
    console.log("somethingTest");
  });
});

Targets, bubbling, propagation and capture

  • Targets

Used to get the event trigger, in this example a click in the window node We can save the event.target into a variable and use it to add elements or change anything

window.addEventListener("click", function(event){
  console.log("Window clicked");
  const eventTarget = event.target;
  console.log(eventTarget);

  eventTarget.setAttribute("data-item", "tomato");
  eventTarget.classList.add("pizza");
});

Or we can access to attributes by using target in the parameter event

const all = document.querySelectorAll("button.buy");

function handleClick (event) {
  console.log("Bought");
  console.log("$" + parseInt(event.target.dataset.price));
    // Access to the dataset "price" and prints it
    // parseInt is used because it was still a string
}

all.forEach(function (button) {
  button.addEventListener("click", handleClick);
})
  • Propagation and bubbling

When something is clicked, the click in reality goes through all the elements and nodes until it reaches the target event

Then it will bubble up triggering clicks on all the elements, so while you are clicking a button you are also clicking the window or other elements above, this could trigger more than one events, one the click of the button and other in the body or window.

To stop that it's possible to use event.stopPropagation();, so when the event happens it won't go up again and trigger the possible events above the element

It's also possible to do the opposite, listen to something in the capture phase before going up using capture, so the upper nodes elements would trigger their events before and not while bubbling up

Note: Add event listener has 3 arguments

target.addEventListener(type, listener[, options]);

This means the first argument is the type, in this example "click", the second argument is the listener which is the function that should be run and the third argument is options, that can include capture, once, passive, etc

function handleClick(event) {
    console.log("window clicked");
    console.log(event.target);
}

window.addEventListener("click", handleClick, {capture: true});

Full example:

const all = document.querySelectorAll("button.buy");

function handleClickButton (awa) {
    console.log("Bought");
    console.log("$" + parseInt(awa.target.dataset.price));
  }

all.forEach(function (button) {
    button.addEventListener("click", handleClickButton);
  }) 

function handleClickWindow(event) {
    console.log("window clicked");
    console.log(event.target);
}
window.addEventListener("click", handleClickWindow, {capture: true});

Because we are using capture, the window function event will be triggered first, after that, the button event will happen

Prevent default

There's elements that have a default use, for example when we click a link it will automatically send us to the website, using preventDefault() will stop this, for example when using twitter and when you click anything instead of going there, it shows a message of login

const hyperlink = document.querySelector(".youtube-link")

hyperlink.addEventListener("click", function(event){
    event.preventDefault();
    console.log(event);
});

Or create a confirmation before going to a page

const hyperlink = document.querySelector(".youtube-link")


hyperlink.addEventListener("click", function(event) {
    const confirmation = confirm(
    "This website might be malicious, do you want to proceed?"
);
if(!confirmation) {
    event.preventDefault();
}
}):

Accesibility gotchas and Keyboard codes

Accesibility is important, some people do not have a mouse so they won't be able to access to parts of your website that listen for "clicks"

Using tab to move around the page does not work in images, we would solve this by adding tabindex=0

<img class="cat" tabindex="0" src="https://picsum.photos/200">
const catPhoto = document.querySelector(".cat");

function handlePhotoClick(event) {
    console.log("Photo clicked");
}

catPhoto.addeventListener("click", handlePhotoClick);
catPhoto.addeventListener("keyup", handlePhotoClick);

This way the event would happen when tabbing onto the image

It's possible to use an if to make it work better by needing them to press enter to go into the image

Note: Event keys are the value of the key pressed, to get more meta data from any key we need to be listened, use: https://keycode.info

    <div role="button" tabindex="0">Something</div>
    <a href="http://youtube.com">
        <img class="cat" src="https://picsum.photos/200" alt="Cat">
    </a>
const catPhoto = document.querySelector(".cat");
console.log(catPhoto)

function handlePhotoClick(event) {
    if (event.type === "click" || event.key === "Enter") {
     ;
    }  
}

catPhoto.addEventListener("click", handlePhotoClick);
catPhoto.addEventListener("keyup", handlePhotoClick);

Flow Control - If Statements, Function Returns, Truthy, Falsy

Statements expect booleans or conditions

If the condition in the parenthesis is true runs the code in the curly brackets right after, if not, it will keep going until it's true from top to bottom

if (10 < 2) {
    console.log("nope")
} else if (11 > 10) {
    console.log("yes")
} // console logs yes since it skips first one

Slugify

Convertir una cadena de texto a un string removiendo o cambiando caracteres

This slugify would replace the s character which is space, for _ a underscore

The first argument is the sentence, and the second if it's true or false, if the second argument is true run the first line which is replace the spaces for underscore and then make it lower case, if it's not true, just replace the spaces for underscore without making it lower case

function slugify (sentence, lowercaseBoolean) {
    if (lowercaseBoolean) {
        return sentence.replace(/s/g, '_').toLowerCase();
    }
    return sentence.replace(/s/g, '_');
}

Return

Return means return a value from a function and STOP that function from running, so it's possible to write it without else in the example above

Another way for the example above:

function slugify (sentence, boolean) {
    let slug = sentence.replace(/s/g, "_"); // Replace the spaces first
    if (boolean) {
    return slug.toLowerCase(); // If true, lowercase it and returns it
    } 
return slug; // If false, just returns the sentence replaced
}

Truthy and falsy

First for the equals

  • 1 equal means assigning something cat = 10 means we are giving cat the value of 10, so it won't give us true or false

  • 2 equals means checking if is the same value and returns true or false cat == 10 checks if a variable or function named cat is equal to 10, this would return true if is the case

  • 3 equals means check if is the same value and the same type cat === 10 would return true, the variable cat contains a number, which makes the variable cat to be a number type, and that comparision is checking the type too. cat === "10" would return false since "10" with quotes would be a string and cat a number

This helps to also check if a variable is null or undefined . if (cat === null) {}

Bang ! makes it the opposite, != means not equal to

10 >= 10 returns true

Truthy

All values are truthy unless they are defined as falsy

Examples:

if(true) {
    console.log("cat") //True, prints cat
}

if(42) {
    console.log("I like pizza") //true, prints I like pizza
}
//or....
if("0")
if([])
if("false")
if(-Infinity)
if(-3.14)

All of this returns true

Falsy

Values considered false, declared as:

if (false) {
  // Not reachable
}

if (null) {
  // Not reachable
}

if (undefined) {
  // Not reachable
}

if (0) {
  // Not reachable
}

if (-0) {
  // Not reachable
}

if (0n) {
  // Not reachable
}

if (NaN) {
  // Not reachable
}

if ("") {
  // Not reachable
}

AND and OR

To represent a condition that has two or more elements to be true or false, we use &&

If cat is 10, and cat2 is 20, and name is "kitty" do this, if either of those variables are not what they are supposed, that part of the code will be skipped

const cat = 10;
const cat2 = 20;
const name = "kitty"

if (cat === 10 && cat2 === 20 && name === "kitty") {
    console.log("This is true")
}

The same way for OR, but instead && we use ||. If any of those variables is true, the code will be run

const cat = 2000;
const cat2 = 20;
const name = "paw"

if (cat === 10 || cat2 === 20 || name === "kitty") {
    console.log("This is true")
} //prints since cat2 is true, even though the rest are not

We can use both together

const name = "john"
const last = "doe"

if (name === "frank" || (name === "john" && last === "doe"){
    console.log("Great name")
}

The logical AND operator &&

If the first operand is truthy, "&&" returns the second operand

true && "dog"; //returns "dog"

Includes method in if

const mode = "danger"
const check = "dangerous".includes(mode);
if (check) {
    console.log("Angry cat")
}

Also works with functions

function isAwesome(name) {
    return "awesome".includes(name);
}
if (isAwesome("some")){
    console.log("it works")
}

Coercion

To convert a truthy or falsy value into a real boolean !! double bang is used, for example a variable that has a string in it is a string, but with !! would convert into a true

const empty = "";

if (empty) {
    console.log("this won't be printed");
}

if (!empty) {
    console.log("This will be printed"); //Bang makes it opposite, its true
}

if (!!empty) {
    console.log("Won't be printed since is a real false")
}

Ternary

Short way for writing a simple if else statement, we use ? as the condition and : as else, to represent else as nothing we use null after the :

1- Condition

2- What to do if true

3- What to do if false

const isCute = true;

isCute ? console.log("How cute") : console.log("Not cute");

Other examples:

const count = 0;
const word = count === 1 ? "item" : "items";

If count is equal to one, give to the word variable the value of "item", if it's not one, word would contain "items"

We can use then after

const count = 1;
const word = count === 1 ? "item" : "items";

const sentence = `You have ${count} ${word} on your cart`;
console.log(sentence); //print "1 item"
const count = 28;
const word = count === 1 ? "item" : "items";

const sentence = `You have ${count} ${word} on your cart`;
console.log(sentence); //print "28 items"

It's possible to push even forward by doing it in the curly brackets

const count = 1;

const sentence = `You have ${count} item${count === 1 ? "" : "s"} on 
your cart`;
console.log(sentence);

Or functions

const isAdmin = true;

function showAdminBar() {
    console.log("Showing admin bar")
}

isAdmin ? showAdminBar : null;

Abusing the condition

When checking a number of functions in a if statement, if one is false the rest won't be activated

function check1() {
    console.log("hi 1")
    return true;
}

function check2() {
    console.log("hi 2")
    return true;
}

function check3() {
    console.log("hi 3")
    return true;
}

if (check1 === true && check2 === true && check3 === true) {
    console.log("All true")
} else {
    console.log("Some checks failed")
}

If one of them is false, the rest won't be executed, so there's a trick to abuse this by using &&

const isAdmin = true;

function showAdminBar() {
    console.log("Showing admin bar")
}

isAdmin && showAdminBar();

This checks if isAdmin is true, if it's true it will run the function, if it's false it won't run

Other way of typing if:

isAdmin = true;

function showAdminBar() {
    console.log("Showing admin bar")
}

if (isAdmin) { showAdminBar() };

Switch case and turtle

The switch statement evaluates an expression, matching the expression's value against a series of case clauses, and executes statements after the first case clause with a matching value, until a break statement is encountered. The default clause of a switch statement will be jumped to if no case matches the expression's value.

const x = 0;
const y = 0;

function handleKeyDown (event) {
    if (!event.key.includes("arrow")) return;
    switch (event.key) {
        case "arrowDown":
            y++
            break;
        case "arrowRight":
            x++   
            break;
        default:    
            console.log("Not valid")
            break;
}
}

Intervals and timers

  • Timers

setTimeout() is a method that takes two arguments, the callback and how many miliseconds it needs to wait in order to run the callback

setTimeout(callback, ms)

setTimeout(function(){
    console.log("It's time")
}, 2000);

It's called a callback because when the code is read, it sets the timer and keep going down the code, then when the timer is off it comes back to run that

If you need to stop the timer you can use clearTimeout()

function compliment() {
    console.log("You are cute!");
}
const cuteTimer = setTimeout(compliment, 2000)

window.addEventListener("click", function (){
    console.log("Stopping compliment")
    clearTimeout(cuteTimer);
})

By clicking the window you'd stop the timer

  • Intervals

Works the same but with how often you want to run the code in miliseconds

setInterval(callback, timesEachMs)

function cat () {
    console.log("I like cats")
}

setInterval(cat, 100); //Will run one time every 100 miliseconds

To stop the intervals works the same, by using clearInterval()



Objects

Everything in JavaScript is an object. Objects are for storing related data, storing functionality or creating your own custom types

  • Allows us to group together properties and values

  • The order of the properties does not matter

  • Properties can have spaces, dashes or start with numbers, unlike variables

  • Is separated by commas

  • Can include nested properties/nested objects

Note: The examples below are creating an object using a constant, the values inside the object can change, this is called mutation. But we can't reasign a whole block {...}of an object since it's a constant

To create an object you open and close a curly bracket, known as objetc literal syntax {}

const age = 100;
const person = {
  age,
  name: "uwu",
  "777": 28,
  "Hello how are you": true,
}
console.log(person);

The values can be any type

If the variable and the propertie have the same name, it can be like this to avoid redundance. This is the same as writing age = age,:

const age = 100;
const person = {
    age, 
    name = "John"
}

Nested properties

Properties that are objects or have more properties to them

const person = {
  name: "Susan",
  age: 425,
  dog: {
    cute: true,
    "dog-name": "paw",
    "dog-age": 11,
  },
  cute: false,
};
console.log(person);

You can add, change or delete values to an object by using dot notation

const person = {
  name: "Susan",
  age: 425,
  dog: {
    cute: true,
    "dog-name": "paw",
    "dog-age": 11,
  },
  cute: false,
};

person.job = "web developer"; //Adding propertie
person.age = 12; //Changing value
delete person.cute // Deletes that property and returns true/false

If you wanna make an inmutable object we can use Object.freeze() this create an inmutable version of that object

const personFreeze = Object.freeze(person);

Square brackets

Used to access a property, similar to the dot notation used all above, it's used as square brackets in order to place variables there, or strings with invalid properties names like "Hello how are you": true;

const person = {
  name: "Susan",
  age: 425,
  dog: {
    cute: true,
    "dog-name": "paw",
    "dog-age": 11,
  },
  cute: false,
};

console.log(person["name"]);

Example of using square brackets vs dot notation

const person = {
  name: "Susan",
  age: 425,
  dog: {
    cute: true,
    "name": "paw",
    "age": 11,
  },
  cute: false,
};

const propertyToCheck = prompt("What property do you want to check?");
console.log(propertyToCheck);
console.log(person[propertyToCheck]);

You can also use dot notation to access nested objects

person.name //returns "Susan"
person.dog.name //returns "paw"

Methods and functions

As explained some time ago, methods are functions that are inside an object. To create one we create a function as a propertie

const person = {
  name: "Susan",
  age: 425,
  sayHello: function (greeting = "hey") {
    return `${greeting} ${this.name}`;
  },
};

To use this method we can use person.sayHello() and it would return "hey Susan" depending on the value we give in the argument

This

If you look at a method and you look to the left of the dot, in the example above thiswould be equal to "person", is used to to be able to use the methods on other objects

Shorthand for creating methods

You can also create a method without function, removing it and removing the colons

const person = {
  name: "Susan",
  age: 425,
  sayHello(greeting = "hey") {
    return `${greeting} ${this.name}`;
  },
}; //Accesed by typing person.sayHello()

Arrow function for methods

It's also possible but you won't have access to the this keyword

const person = {
  name: "Susan",
  age: 425,
  sayHi: () => {
    console.log("Hi!");
  }
}; //Accesed by typing person.sayHi()

Object Reference vs Values

When comparing objects, it references to the object itself, not the values inside of it. This means that even if two objects have the exact same properties and values, it won't be true when comparing

let cat = "cat";
let cat2 = "cat";

cat === cat2 ? console.log("cat true") : console.log("No");
//prints "cat true" since the value in the variable is the same

const catObj = {
  hair: true,
  cute: true,
  gender: "Male",
}
const catObj2 = {
  hair: true,
  cute: true,
  gender: "Male",
}

console.log(catObj === catObj2); 
//prints false since it's comparing the object, not the values

When using variables, we can update a variable by making a copy of other one like this

let cat = "cat";
let cat2 = "notACat"

cat2 = cat;
console.log(cat2);

This changed cat2 variable to be "cat" using the first cat variable

But when using objects doing the same thing could lead to bugs , since it does not create a copy of the other one, instead it references the first object

const catObj = {
  hair: true,
  cute: true,
  gender: "Male",
}

const catObj2 = catObj;

This might not seem like a problem at first, but if we change values inside the new object created they will change in the first object too

const catObj = {
  hair: true,
  cute: true,
  gender: "Male",
}

console.log(catObj); // object 1 hair value is true here
const catObj2 = catObj; // creating object 2 "with same values as obj 1"
catObj2.hair = false; // hair value changed to false in object 2
console.log(catObj2); // hair value is false
console.log(catObj); // hair value is false without updating object 1

This can lead into bugs, since when updating object 2, we also changed object 1 without wanting to do it. This is because it's a reference

Copying objects - Spread operator

To avoid the problem above, we use something called spread

Spread is the three dot operator and is used to take every item inside and object and spread it into another

const catObj = {
  hair: true,
  cute: true,
  gender: "Male",
}

const catObj2 = { ...catObj};

It's possible to spread from two objects into one or add new ones

const pizzaIngredients = {
  tomato: true,
  cheese: true,
  pineapple: false,
}

const hamburguer = {
  beef: true,
  lettuce: true,
  onion: "never",
}

const inventory = { ...pizzaIngredients, ...hamburguer, bread: 10};
console.log(inventory);

Functions inside objects also might overwrite something when passing an object to them and modyfing that object, the external will also be updated. So you need to make sure to pass it as a copy


Shallow and deep clones

But when doing nested objects, we run into the same problem

Spread is a shallow copy, only copy the first layer is copied, if we have another object we run into the exact same problem from above, to avoid this we need to use a utility library

  • Recommended: Lodash

Using Lodash library we can create a deep copy of an object by using the _.cloneDeep() method

const catObj = {
  hair: true,
  cute: true,
  gender: "Female",
  kitties: {
    "Has kitties": true,
    number: 7,
  },
}

console.log(catObj.kitties["Has kitties"]) // true
const catObj2 = _.cloneDeep(catObj); // cloning obj1 into the new obj2
catObj2.kitties["Has kitties"] = false; // Making an obj2 value false
console.log(catObj2.kitties["Has kitties"]) // false, obj2 value is false
console.log(catObj.kitties["Has kitties"]) // true, obj1 was not changed

Maps

Is a collection of data identified by keys, the main difference to an object is that map allows keys of any type.

The order here matters, and is another reason why using maps and not objects

Can place an object as the key and add a description for that object

Methods and properties are:

  • new Map() - creates the map
  • map.set(key, value) sets a value specified for a key
  • map.get(key) - Returns the key value. Undefined if it does not exist
  • map.has(key) - returns true if the key exist in the map, false if it doesn't
  • map.delete(key) - Removes an element with that key
  • map.clear() - Removes everything from the map
  • map.size - Size, returns the actual quantity of elements

Creating a map with the keys we want in the way we want, then we give them values.

It's possible to also include an object as a key, and give it a value too:

const person1 = {
  name: "John",
  age: 2412,
}

const myMap = new Map();
myMap.set("name", "Husk");
myMap.set(100, "This is a number");
myMap.set(person1, `${person1.name} is really cool`)

Another example:

let score = 500;
const prizes = new Map();
prizes.set(100, "old CD");
prizes.set(300, "teddy bear");
prizes.set(500, "amazing cat plushie");
console.log(`You won a ${prizes.get(score)}`); 
// "You won a Amazing cat plushie"

Example with an if to practice and make the syntaxis correct

If the score is 100, or 500, add "n" for it to be correct: "an old CD...", "an amazing cat..."

let score = 500;
const prizes = new Map();
prizes.set(100, "old CD");
prizes.set(300, "teddy bear");
prizes.set(500, "amazing cat plushie");
console.log(`You won a${score === 100 || score === 500 ? "n" : ""} ${prizes.get(score)}`);

Arrays

Arrays are similar to a list where we can do

The Array object, as with arrays in other programming languages, enables storing a collection of multiple items under a single variable name, and has members for performing common array operations.

  • item - Each thing inside an array

  • index - Position in the array of an item

  • length - Number of items inside an array

  • Each item can be any type

When using typeof on an array, you'll get object, because arrays don't have their own type, to check if something is an array use the Array.isArray() method, returns true or false

To create an array we use Square Brackets [] this is known as an array literal

const names = ["Paw", "Moon", "Appa"];  

We can also create one by this

const names = new Array();

To access an item of an object, we use its index number

const names = ["Paw", "Moon", "Appa"];  

console.log(names[0]); // Paw
console.log(names[1]); // Moon
console.log(names[2]); // Appa
console.log(names.length); // 3

It's really important to know that arrays start from 0, not from 1, so in order to get the first item we access using 0

To access for example the last item of a large array, we can say

const names = ["Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa",];
console.log(names[names.length- 1]); // Appa

Because names.length would have a value of 3, but we can't access 3 since the last index is 2, because it starts in 0

Mutable and immutable

  • Mutation refers to when something changes the original version of it

  • Immutable refers to the methods not chanting the original version, instead return a new array

Some methods will modify the original array when some will create a new array and leave the original as it.

Note: For react, is a good to when using React, don't change the original version, instead create a copy

Mutation and keep the original the same

.slice() method takes as argument the element where you start and the element where it ends without counting that last one

// Mutation method
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
numbers.reverse(); // Reverts the data, 9,8,7...
console.log(numbers); // The original was changed, 9,8,7,6...

// Immutable
const slice = numbers.slice(2, 4); //Took a slice of an array out
console.log(slice); // [3, 4]
console.log(numbers); // 1,2,3... Wasn't changedd

In order to use a Mutable Method but keeping the original array as it is, we need to Spread to first create a copy, then edit that one

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // First array

const numbersReversed = [...numbers]; // Create a copy
numbersReversed.reverse(); // Change the values of the copy
console.log(numbersReversed); // [9,8,7,6...] changed
console.log(numbers); // [1,2,3,4...] wasn't changed

You can also do this:

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // First array

const numbersReversed = [...numbers].reverse(); // Create a copy

Some common methods for Arrays

  • .push() - Add an item to the array, in last position
const languages = ["Hello", "JavaScript", "Python", ];
const newLanguages = [ ...languages, "C", "Java"] //Make a copy and push C and Java
newLanguages.push("C++"); 
console.log(newLanguages); //['Hello', 'JavaScript', 'Python', 'C', 'Java', 'C++'
  • .unshift() - Add an item, in first position

  • .pop() - Deletes the last item in the array

  • .shift() - Deletes the first item

  • .findIndex() - Used to find a particular element in an array, loops over until it finds it and returns true. There's a longer way to do it that makes a bit more sense.

    1st it loops over how many items are in the array, then takes a callback function and that checks if the item is the same, if it is it returns true and gives the position, if false it skips to next one

const names = ["John", "Samuel", "Yeseo", "Juan", "Hello"];
const samuelIndex = names.findIndex(name => name === "Samuel"); //true
console.log(samuelIndex); // 1
console.log(names[samuelIndex]); // Samuel

//Larger version
names.findIndex(function (name) {
    if (name === "Yeseo") {
        return true;
    } return false;
}); // 2
  • .includes() - Checks wether the array contains the specified element

  • .concat() - Join two different arrays from end to start

const firstArray = ["Butterflies", "Moonlight", "Sunrise",];
const secondArray = ["Cat"]

const bothArrays = firstArray.concat(secondArray);
console.log(bothArrays); // ['Butterflies', 'Moonlight', 'Sunrise', 'Cat']
  • .forEach() - Runs a loop once for each element in the array
const newArray = ["Cat", "John", "Angel"];
newArray.forEach(function (element) {
    console.log(element); // Cat, John, Angel
});
  • .sort() Sorts in alphabetical order in ascending order

  • .splice(start[,deleteCount]) - Deletes existing methods and allows to add too. First argument is where it starts, the second argument is how many it will delete from there

const numbers = [1,2,3,4,5,6,7,8,9,];
numbers.splice(3, 4);
console.log(numbers); //[1,2,3,8,9] started from 3 and deleted 4 from there
  • .slice() method takes as argument the element where you start and the element where it ends without counting that last one

Add an item in between

Using slice we place the positions of the items we want, then add a new one, then slice again to add the rest

const languages = ["Hello", "JavaScript", "Python", "C", "Java",];
const newLanguages = [
  ...languages.slice(0,2), // Adds "Hello" and "JavaScript
  "C++", // Adds "c++"
  "SQL",
  ...languages.slice(2), // Adds the rest starting from 2
];
console.log(newLanguages);

Remove an item in between

To remove an item we can simply make a copy without it

const languages = ["Hello", "JavaScript", "Python", "C", "Java",];

const newLanguages2 =[
    ...languages.slice(0,2),
    ...languages.slice(3),
]

In a larger array or when using React is pretty common to

const names = ["John", "Samuel", "Yeseo", "Juan", "Hello"];
const yeseoIndex = names.findIndex(name => name === "Yeseo");

const namesWithoutYeseo = [
  ...names.slice(0, yeseoIndex), //from 0 to the Yeseo index
  ...names.slice(yeseoIndex+1) // From Yeseo index + 1, until it ends
];

console.log(namesWithoutYeseo);

Array Static Methods

Static methods are for creating or working with arrays, they

  • Array.of()

Used to create an array

Array.of("John", "Doe",)
//Returns an array with those items

  • Array.from()

Creates an array with empty or undefined slots first argument takes an object with the length. The second argument takes a map function to tell us what can go inside those slots

const empty = Array.from({length: 10});
console.log(empty);
const range = Array.from({length: 10}, function () { //Create array 10 empty slots
        return "Hello"; // Fills the slots with the return
    });

Or get the index the same way, we place an underscore to reference that that argument will not do anything, but it needs to be placed since that map function takes two arguments

 const range = Array.from({length: 10}, function (_item, index) {
        return index; //Returns
    });

You can also use a function to create a range that you can decide to create an array from a number you decide to another, in example 4-100 would create an array that goes from 4 to 100

function createRange(start, end){
  const range = Array.from({length: end - start + 1}, 
  function (_item, index) {
    return index;
 });
    return range;
 }

  • Array.isArray()

Usually we use typeof but an array is an object type so we would get an object, in order to check if an array is an array, we need to use the Array.isArray() method

const rangeArray = createRange(4,100); // Following the previous examp.
console.log(Array.isArray(rangeArray)); // True

  • Object.keys()

  • Object.values()

  • Object.entries()

Used to get keys, values or both of an object and place it in arrays

const meatValues = Object.values(meats); // Array with object values 
const meatKeys = Object.keys(meats); // Array with object keys
const meatEntries = Object.entries(meats); // Array with both

Array - Instance Methods

  • .join()

Separates the items of an array into a string

const buns = ['egg', 'wonder', 'brioche'];

console.log(buns.join(" or ")); // "egg or wonder or brioche"
console.log(buns.join(" - ")); // "egg - wonder - brioche""
  • .split()

Takes a string and makes it an array, replacing a character with a separation for the arrays (in this example, comma)

const foodString = "hot dogs,hamburgers,sausages,corn";
const foodArray = foodString.split(","); //Split and replace comma
console.log(foodArray); //Array with "hot dogs, "hamburguers"... elements
  • .pop()

Takes the last item of an array, but it returns that item, so it can be stored to be used later

  • .push()

Place an item as the last one inside an array. Returns the updated number of items after adding that last item

const toppings = ['Mushrooms ', 'Tomatoes', 'Eggs', 'Chili', 'Lettuce', 'Avocado', 'Chiles', 'Bacon', 'Pickles', 'Onions', 'Cheese'];
const lastItem = toppings.pop(); //Takes the last item, mutating the array

topping.push(lastItem); //place the item again since was stored "lastItem"
console.log(topping.push(lastItem)); // 11, returns the number of items
  • .shift()

Takes the first item of an array, returns that item

  • .unshift()

Places an item as first in the array. Returns the updated number, same example as above

Example adding and removing items while doing it inmmutable.

Some examples and exercises with arrays:

  • Adding and removing items without changing the original
let toppings2 = toppings.slice(0, toppings.length - 1); //Remove last
toppings2 = [...toppings2, toppings[toppings.length - 1]]; //Reinsert last
toppings2 = [...toppings.slice(1, toppings.length)]; // Remove first 
toppings2 = [ toppings[0], ...toppings2]; // Reinsert first
  • Copy using slice, another way without using spread
const toppingsCopy = toppings.slice(0); 
//If not given second argument, goes until the end of the array
  • Using indexOf to find
const avocadoIndex = toppings.indexOf("Avocado");
console.log(avocadoArray); //5
  • Using includes and adding it if it does not include it
console.log(toppings.includes("hot sauce")); //false

if (toppings.includes("hot sauce")) {
  console.log("")
} else toppings.push("hot sauce");
  • Using .reverse() mutating the original, to not mutate create another using spread
toppings.reverse();
console.log(toppings);

Methods using functions as arguments.

Iterative functions

Many array methods take a callback function as an argument. The callback function is called sequentially and at most once for each element in the array, and the return value of the callback function is used to determine the return value of the method. They all share the same signature:

Take 3 arguments: element``index, array, and thisArg

  • find()

Returns the first item that satisfies the provided testing function. Takes a callback as a parameter, the callback can

const feedback = [
  { comment: "Love the burgs", rating: 4 },
  { comment: "Horrible Service", rating: 2 },
  { comment: "Smoothies are great, liked the burger too", rating: 5 },
  { comment: "Ambiance needs work", rating: 3 },
  { comment: "I DONT LIKE BURGERS", rating: 1 },
];

function findBurguer(firstFeedback) {
  if(firstFeedback.comment.includes('burg')){
    return true;
  } else {
    return false;
    }
 };

const burguer = feedback.find(findBurguer);
console.log(burguer);

One line way:

const burguer = feedback.find(burgs => burgs.comment.includes("burg"));
console.log(burguer);

.find will run for each element in the array until it gets a true, then will return the value of the first item that gave true, burgs represents a parameter and can change, but it's needed to access correctly to the feedback.comment.includes("burg")

Creating a function that will take a string as an argument and then when using it we will be able to look for the word we want

function findItem(text) {
  return function (singleFeedback) {
    return singleFeedback.comment.includes(text);
  };
}

const aokay = feedback.find(findItem("burg"));
const aokay2 = feedback.find(findItem("Smooth"));

Functions like find() and filter() it's really helpful to name them extremely explicit, because many times you will use them in other files

.filter()

Loops every element and returns true or false

const ratingsAbove2 = feedback.filter(itemOfTheArray => {
  if (itemOfTheArray.rating >= 2){
    return true;
  } else {
 return false;
  }
});

console.table(ratingsAbove2);

Compact way, since this if only evaluates if it's true or false, removing the return true and return false, and just returning the evaluation

const ratingsAbove2 = feedback.filter(itemOfTheArray => {
  return itemOfTheArray.rating >= 2;
});

console.table(ratingsAbove2);

Or another way that would be more usual, a single line

const ratingsAbove2 = feedback.filter(itemOfTheArray => itemOfTheArray.rating >= 2);
console.table(ratingsAbove2);

The same example of find but using filter:

const ratingsBurguer = feedback.filter(item => item.comment.includes("burg"));
console.table(ratingsBurguer);

Removing an element or elements using find or filter

const ratingsLow = feedback.filter(itemOfTheArray => itemOfTheArray.rating < 2);
console.table(ratingsLow); //Shows the items below rating 2
feedback.pop(ratingsLow); // Removes them
console.log(feedback); // Feedback mutated and now does not include

.some()

Tests wether at least one element in the array passes the test implement by the provided function, returns true or false

const meats = {
  beyond: 10,
  beef: 5,
  pork: 7,
};

const isThereEnoughOfOneTypeOfMeat = Object.values(meats).some(meatQuantity => meatQuantity >= 5);
console.log(isThereEnoughOfOneTypeOfMeat); //true

.every()

Makes sure every single element in the array meets our criteria

const isThereEnoughEveryMeat = Object.values(meats).every(meatQuantity => meatQuantity >= 3);
console.log(isThereEnoughEveryMeat); 

.sort()

Sorts elements of an array, converts it into strings and compare their sequences of UTF-16 code units values (alphabetically)

Takes as argument a callback function to compare and decide wether something will be in front or something or behind

const numbers = [1,2,5,100,500,242,900,1952,7,4,2];
const sortNumbers = numbers.sort(function (firstItem, secondItem){
  return firstItem-secondItem;
});

Looping and iterating

Most common is looping over an array, does not return anything

.forEach()

Loops and runs the callback function for each element in the array, object or variable. Takes:

  • callback function as a parameter

  • currentValue - The reason we need to give an argument inside, is because this translates into the currentValue of the array, if we name this parameter as item or whateverIDK, it still means the value of the array. In example if we have an array const myArray = [1,4,5,"cat"]; when using that parameter we named before, it has the value of 1, then 4, then 5, then "cat"

  • `index`` - (optional) Gives the index number of the element

  • array ` - (optional) Gives the complete original array

const toppings = [
  "Mushrooms ",
  "Tomatoes",
  "Eggs",
  "Chili",
  "Lettuce",
  "Avocado",
  "Chiles",
  "Bacon",
  "Pickles",
  "Onions",
  "Cheese",
];
function logToppings(currentTopping) {
    console.log(currentTopping)
}

toppings.forEach(logToppings);

Map, filter and Reduce

Side effect is when you are in the scope of a function and you reach outside to do something else, like attaching an event listener or put something on the page

Map

Creates a new array with the results provided by a function Takes in data, performs an operation and gives it on the other side, always produce the same length of the array

  • Transforms each piece of that data and then returns it
const faces = ['😃', '🤠', '🤡', '🤑', '😵', '🌞', '🐶', '😺'];

function addArms(face) {
  return `🤚 ${face} 🤚`
}
const toys = faces.map(addArms);
console.table(toys); //A Table wich each face but with added hands

or

const familyDoe = ["John", "Johana", "Karina", "Winter"].map(itemValue => `${itemValue} Doe`)

Same example but capitalizing a letter using two maps together

function capitalize(item) {
    return `${item[0].toUpperCase()}${item.slice(1)};`
   }

   const familyDoe = ["John", "johana", "Karina", "winter"].map(capitalize).map(itemValue => `${itemValue} Doe`)

So map will always take in an item, do some work with it and return the new value

More map examples

const orderTotals = [342, 1002, 523, 34, 634, 854, 1644, 2222];
const orderTotalsChange = orderTotals.map(total => 1);
//returns an array with 1,1,1,1,1,1,1 since it replaces the element of
// the array with the return of the map

Map can also be used with any type of data. You will more likely get objects that come from an API

Since they come from an API, not always they will be in the format that we want, so we can change it

    const people = [
      {
        birthday: 'April 22, 1993',
        names: {
          first: 'Keith',
          last: 'Buckley'
        }
      },
      {
        birthday: 'January 3, 1975',
        names: {
          first: 'Larry',
          last: 'Heep'
        }
      },
      {
        birthday: 'February 12, 1944',
        names: {
          first: 'Linda',
          last: 'Bermeer'
        }
      }
    ];
   const cleanPeople = people.map(function cleanse(person){
    const birthday = new Date(person.birthday).getTime();
    const now = Date.now();
    const age = Math.floor((now - birthday) / 31536000000);

    return {
        age, // Short of: `age = age,`
        name: `${person.names.first} ${person.names.last}`
    }
   });
   console.log(cleanPeople);

Since the dates were strings and the names were separated, we converted the string and date into a more real date

Filter

Creates a shallow copy of a portion of a given array, checks every item in the array if it's true it goes into the copy, if it's false it doesn't

Parameters:

  • callback function

    • element

    • index

    • array

const words = ['spray', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
//Only words with more than 6 letters
  • .find()

To find an item, returns an object

    const students = [
      {
        id: '11ce',
        first_name: 'Dall',
        last_name: 'Puckring',
      },
      {
        id: '2958',
        first_name: 'Margarete',
        last_name: 'Brandi',
      },
      {
        id: '565a',
        first_name: 'Bendicty',
        last_name: 'Woodage',
      },
    ];
const margarete = students.find(studentID => studentID.id === '2958');

Function to find properties of an object using find:

function findByProp(prop, propWeAreLookingFor) {
  return function (item){
    return item[prop] === propWeAreLookingFor;
  }
};
const student1 = students.find(findByProp("id", "2958"));
const student2 = students.find(findByProp("last_name", "Woodage"));
console.log(student1);
console.log(student2);

We create a function that takes two argument, the key and the value, and then we compare if the item we are looking corresponds to the property we game. We used square brackets since it is a property

Reduce

Takes an amount of data and returns a result or a single value. Takes two arguments:

  • callbackFunction

    • accumulator (Value result of the previous call to callbackFn)

    • currentValue (The value of the current element)

  • initialValue (optional, a value which accumulator is initialized)

Syntax:

reduce(callbackFn, initialValue);
const orderTotals = [342, 1002, 523, 34, 634, 854, 1644, 2222];

function tally(tally, currentPrice){ //accumulator and current value
  return tally + currentPrice;
} 

const tallyTotal = orderTotals.reduce(tally , 0); //0 is initial value
console.log(tallyTotal);

The callback function takes an accumulator and the currentValue,

For, For of, For in and While loop

For

Creates a loop that consist in three optional expressions, enclosed with a parenthesis, followed by a statement to be excecuted in each loop

Syntax

for(initialization; condition; afterthought) {statement}

Basic for example

for(let i = 0; i <= 10; i++) {
    console.log(i);
}

For with arrays

const numbers = [1,4,5,2,5,63,78,0];

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

For of

Used for looping over iterables, it can handle emojis. If you need to sequence data, one thing another in a loop, for of is very handy for sequency promises

Iterables are something that has a length

const name = "Cat";
for(const letter of name) {
    console.log(letter);
} // 

For in

Used for looping over keys of an object

const guy = {
    name: John,
    age: 19,
    "okay": true,
}

for(const property in guy) {
    console.log(property); //name, age, "okay"
}

While loop

Will take a condition and run infinitely until the condition is false, this can crash the tab or navegator, so yo need an exit condition

let cool = true;
let i = 1;
while (cool === true) {
    console.log("you are cool");
    i++
    if(i > 100) {
    cool = false; //Exit condition
    }
}

Do while loop

Similar but excecutes the code at least once, since the condition is read after excecuting the statement


This Keyword

Important notes for this section:

  • A function that makes an object is called a constructor

  • Inheritance and instances refers to passing down characteristics from a parent to a child so that a new piece of code can reuse and build upon the features of an existing one

  • this refers to the instance of the thing that was made. this refers to the object that the function is accessed on and changes for every function or scope its in, for example
const button1 = document.querySelector(".button-one");

function tellMeAboutTheButton() {
    console.log(this); // will print the <button> tag of button1
}

button1.addEventListener("click", tellMeAboutTheButton);
// 'this' will refere to button1, the object in which the function is
// accesed on

this is also used to store information inside that instance

// Using constructor to create a template object
// This object takes two arguments as parameters, an array of toppings and a customer
function Pizza(toppings = [], customer) {
  console.log(this); // Pizza object
  // Store information from the argument into the object Pizza
  this.toppings = toppings;
  //this.customer creates the template, customer is the argument we are getting the info from
  this.customer = customer;
  // Just random ID
  this.id = '#' + Math.floor(Math.random() * 16777215).toString(16); 
}
//Creating an instance of Pizza, using different arguments
const pepperoniPizza = new Pizza(["Pepperoni", "Cheese,", "Tomato Sauce"], "John Doe");
const hawaianaPizza = new Pizza(["Pineapple", "Cheese,", "Tomato Sauce"], "Jane Pineapple Doe, the lover of pineapple pizza");

pepperoniPizza and hawaianaPizza are an instance of 'Pizza' and they have their own unique information.

When this is invoked as a standalone function (not attached to an object: func()), it will refer to the global object

  • When using an arrow function, this inherits from the parent scope at the time they are defined, this is useful for callbacks

Arrow example to change the button content using this

const button1 = document.querySelector(".button-one");

function changeButtonText() {
  console.log(this); // <button class="button-one">Test</button>
  setTimeout(() => {
    this.textContent = `Button clicked`; // 'this' still refers to the button
  }, 1000); //Changed the button content from 'Test' to 'Button clicked'
}

button1.addEventListener('click', changeButtonText)

Because we are using an arrow function, the this inside that will still refer to the button1 tag, if we hadn't used an arrow and made a normal function, this would've changed and it would not work

New Keyword

The new operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.

Syntax

new Constructor()

We use new here to create other instances from the parent Object Car and store it in another variable

// Blueprint for other Objects with this same properties
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
//'new' here creates a new instance of the Object Car, with the properties
const car1 = new Car('Eagle', 'Talon TSi', 1993);
const car2 = new Car('idk', 'Ferrari yes', 2018)

console.log(car1);
// Car {make: 'Eagle', model: 'Talon TSi', year: 1993}

console.log(car2);
// Car {make: 'idk', model: 'Ferrari yes', year: 2018}

a


Prototypes and prototypal inheritance

Every object in JavaScript has a built-in property, which is called its prototype

This is an object with one data property:

const myObject = {
  city: "Canada",
  greet() {
    console.log(`welcome to ${this.city}`);
  },
};

myObject.greet(); // welcome to Canada

If we type myObject in the console, we will be able to access to a list of all properties availabe for this object, besides the ones we created, for example toString()

  • Prototype lookup or chain

When we call myObject.toString(), the browser:

  • looks for toString in myObject
  • can't find it there, so looks in the prototype object of myObject for toString
  • finds it there, and calls it.

Each type of data shares different functionalities in the protoype, for example all arrays share the .filter method

Creating a prototype

It's useful to create a function which is stored in prototype, because when we use a function inside an object we are creating a copy everytime, that's fine for some objects and methods, but when we are dealing with creating 20,000 pizzas it will be slower

Object.prototype.functName

Creating a function of eat following the same example of the this section

function Pizza(toppings = [], customer) {
  console.log(this);
  this.toppings = toppings;
  this.customer = customer;
  this.id = "#" + Math.floor(Math.random() * 16777215).toString(16);
  this.slices = 10;
}

// Creating prototype level function
Pizza.prototype.eat = function () {
  if (this.slices > 0) {
    //'this' is the Pizza object, accessing to slices
    this.slices = this.slices - 1; // Removes one slice
    console.log(`Ñom Ñom`);
    return `${this.slices} left`; // Slices left
  }
  return "No more pizza"; // When slices is 0, no more pizza is returned
  };

const pepperoniPizza = new Pizza(["Pepperoni", "Cheese,", "Tomato Sauce"],"John Doe");
const hawaianaPizza = new Pizza(["Pineapple", "Cheese,", "Tomato Sauce"],"Jane Pineapple Doe, the lover of pineapple pizza");

This way this function is created only once and lives in the proto

So when using the Object Pizza 20,000 times, it won't create a function and run it each time, we can access it in the console or our code editor by typing:

pepperoniPizza.eat();

Like if we use any other proto function before

Classes

Added in 2016 to browsers so it's a bit new

classes are a template for creating objects. They encapsulate data with code to work on that data

// Declaration
class Rectangle {
  constructor(height, width) { //Parameters of what will take as arguments
    this.height = height;
    this.width = width;
  }
} // classes don't use semicolon... For some reason

Following the same example above, classes work similar to the Objects

class Pizza {
  constructor(ingredientes=[], customer) {
  this.ingredientes = ingredientes;
  this.customer = customer;
  }
}

const meatPizza = new Pizza(["meat", "another kind of meat"], "MeatLover3000");

Bind, call and apply

Common for interviews

They change the scope of what this keyword is equal to, inside of a function

  • bind()

this is always defined where the function is being called and NOT where it was defined, to change this we can use bind

const whiteCat = {
    isWhite: true,
    age: 1000,
    name: "baby cat",
    bite() { // Method of whiteCat, `this` here is whiteCat
      return `You got bit by ${this.name}`;
    }
};

const angryDog = { name: "angry dog"}

// Bind 'this' from the bite methond inside of whiteCat, into angryDog object
const bite = whiteCat.bite.bind(angryDog); 

Usually you can't just do something like store document.querySelector() in a variable, and then reuse that variable as a replacement from the querySelector

const $ = document.querySelector;
console.log($("body")); // Error: Illegal invocation

That's because querySelector needs to know where to look inside of in order to find the element we are looking for, it knows where to look because it uses this as the document

bind() to create a short version of existent Methods

We can use bind to change the this in order to be able to do it

// Manually change this to "document"
// By using bind against querySelector, we say when the function $
// is run, use `document` as the `this` value
const $ = document.querySelector.bind(document);
// Store the document.querySelector in a variable
const paragraph = $("p");
console.log(paragraph);

This way we made a shorter way to do document.querySelector() by only writting $()

Useful to creat libraries that has shortcuts, we can instead of saving it into a variable, we can save it into a function a do some more changes inside that function

  • call()

Does the same thing as bind, but it aditionally runs the function

  • apply()

The same as call, but it takes an array as argument

Full example of this

const prices = {
    total: 1000,
    calculateTax (taxRate) {
        return this.total + (this.total * taxRate)
    },
    totalMeal (mealType, drinkType, taxRate) {
        return `You bought ${mealType} and ${drinkType}, your total is ${this.calculateTax(taxRate)}`
    }
};
const price1 = prices.calculateTax.bind(prices);
console.log(price1(0.15));

// we can manually add an object, and the second argument will be taken as the argument
const price2 = prices.calculateTax.call({total: 500}, 0.15); 
console.log(price2); // No need to call it

const newPrice = {
    total: 5000,
};

// Same but using a single array as argument
const price3 = prices.calculateTax.apply(newPrice, [0.32]);

// Calculate totalMeal, prices is needed to reference "this" and the array for the other parameters
const bigMeal = prices.totalMeal.apply(prices, ["pasta", "expensive water", 0.15])
console.log(bigMeal);

Callback hell

Is caused when using many callbacks one after another

When we use something like setTimeout() the programm waits for those seconds in something called "Web API", if we use many callbacks or timeouts nested

const div = document.querySelector(".go");

function clicked () {
    div.textContent = "Go";
    setTimeout(() => {
        div.classList.add("circle");
        setTimeout(() => {
            div.classList.add("red");
            setTimeout(() => {
                div.classList.remove("circle");
                setTimeout(() => {
                    div.classList.remove("red");
                    div.classList.add("purple");
                    setTimeout(() => {
                        div.classList.add("fade");
                    }, 500);
                }, 300);
            }, 250);
        }, 500);
    }, 2000);
}

div.addEventListener("click", clicked);

Promises

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

For example a data being returned from an API or someone giving access to a webcam or a microphone in the website. What we often get when we request those things is not the immediate data back, instead we get a Promise. A promise of maybe getting data, or not getting it which is called Rejecting

Used with Async and Await

When you mark a function with async, that function will return a promise

Promise Object

Takes a callback with two arguments: resolve function and reject function

Promise(function(resolve, reject) {}):

Example for creating a Pizza when getting true from a fake outside

let someAPIData = true;

function makePizza(toppings) {
    // Promise of getting something
    const promisedPizza = new Promise(function (resolve, reject) {
    // If we get true from the api data, resolve this promise after .9 sec
        if(someAPIData) { 
          // Resolve
          setTimeout(() => {
            resolve(console.log(`Pizza made with the toppings: ${toppings}`));
          }, 900);
          // If we don't, reject
        } else { 
            // Reject
            reject(console.log("Failed to obtain confirmation"));
        }
    });
    return promisedPizza;
} 
  • .then()

The .then() method takes up to two arguments; the first argument is a callback function for the fulfilled case of the promise, and the second argument is a callback function for the rejected case. Each .then() returns a newly generated promise object, which can optionally be used for chaining

function makePizza2(toppings) {
  // Short way of returning the new Promise object
  return new Promise(function (resolve, reject) {
    //Resolve
    setTimeout(() => {
      // Using join to be able to take an array of toppings
      resolve(`Here's your pizza with the toppings: ${toppings.join(" ")}`);
    }, 1400);
    //Reject
  });
}

makePizza2(["Pepperoni"])
  .then(function (resolvedCase) {
    console.log(resolvedCase);
    return makePizza2(["cheese", "tomato", "pineapple"]);
  })
  .then(function (resolvedCase) {
    console.log(resolvedCase);
  });

Take a closer look for the .then() method, it takes a callback as the first argument and then returns a new promise with new arguments, in order to create a chain

Similar example but making the timeout slower for each added topping

function makePizza3 (toppings = []) { // Default empty array
  return new Promise(function (resolve, reject) {
    // For each topping add 200 miliseconds
    const timeOfBake = 500 + (toppings.length * 200);

      setTimeout(() => {
        resolve(`Hi ${toppings.join(', ')}`)
      }, timeOfBake);
  }) 
}
makePizza3(["Bread", "pepperoni", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese"]).then(function(fullfiled) {
  console.log(fullfiled);
})

Promise.all( )

It's possible to wait for a number of promises to be fulfilled, here we need to wait for the cheese, pepperoni and vegan pizzas to be done before sending anything, we can do it using Promise.all, which takes an array, then we can use then to, when the promised is resolved, do something like print it

Promise.all returns an array

function makePizza3 (toppings = []) {
  return new Promise(function (resolve, reject) {
    const timeOfBake = 500 + (toppings.length * 200);

      setTimeout(() => {
        resolve(`Hi ${toppings.join(', ')}`)
      }, timeOfBake);
  }) 
}
const cheese = makePizza3(["Bread", "pepperoni", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese"]);
const pepperoni = makePizza3(["Bread", "pepperoni"]);
const veganChoice = makePizza3(["lettuce", "(I'm sorry)"]);

const allPizzas = Promise.all([cheese, pepperoni, veganChoice])
.then(finished => {
  console.log(finished);
})

Promise.allSettle()

Similar to Promise.all() but if one of those promises breaks or is rejected, it won't stop and will keep going

Reject function in promises

  • catch

It only runs when the Promise does not go succesfully, its used to handle the error of not getting a promise fullfiled, when we call a function that has a promise inside, we will use then to do something with the fulfilled promised, and catch for handling the error

Takes a function as an argument

function makePizza(toppings) {
  // Promise of getting something
  const promisedPizza = new Promise(function (resolve, reject) {
    // If we get true from the api data, resolve this promise
    if (someAPIData) {
      // Resolve
      setTimeout(() => {
        resolve(`Pizza created with the toppings: ${toppings}`);
      }, 900);
      // If we don't, reject
    } else {
      // Reject
      reject(console.log("Failed to obtain data"));
    }
  });
  return promisedPizza;
}

makePizza("pepperoni")
  .then(function (resolvedData) {
    console.log(resolvedData);
    return makePizza("Cheese");
  })
  .then(function (resolve) {
    console.log(resolve);
  }).catch(function(error) { // Place the catch at the end of the chain of thens
    console.log(error);
    console.log("Need access to the API Data");
  })

Wait package

Short way of creating a promise that will take different amounts of time

function wait(ms = 0) {
  return new Promise(function(fulfill, reject) {
    setTimeout(fulfill, ms);
  })
}

wait(200).then(() => {
  console.log("test");
})

Async and Await

  • Await

The await operator is used to wait for a Promise and get its fulfillment value. It can only be used inside an async function or at the top level of a module.

Await makes an async function wait for a promise

When using await inside a function, the cascading stops and when the promised is resolve, it continues

function wait(ms = 0) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve, ms);
    })
}

async function go () {
    console.log("1");
    await wait(1000); // Wait until 1000 ms pass, then do the next line
    console.log("2");
    await wait(1000);
    console.log("3");
};
go();
  • Async

Asynchronous means that this function waiting time does not interfere with others

For await to work the function needs to be an async function. Ways to declare async to a function for the different types of functions:

// Function declaration
async function something() {}

// Arrow function
const arrowFn = async () => {}

// Callback function
window.addEventListener("click", async function() {});

// Methods or functions inside objects
const person = {
    name: "Juan",

    // Method
    bite: async function() {
        console.log("bite");
    },
    // Method shorthand
    async smile() {
        console.log("smiling");
    },
    // Function property
    sayHi: async () => {
        console.log("hi");
    }
}

Await also works to return the content of a promise instead of the promise itself


Async await error handling

  • Try catch

Is used to try a block of code and if it throws an error, the code in the catch will be executed. It works with any error and makes it so JavaScript does not break when it finds an error.

When it finds an error inside the try block, stops reading what's below and jumps into the catch

Syntax:

try {
    // Code to be tested
} catch (exceptionVariable){ // Commonly used "error", "err", "e"
    // catch statements 
}

Example

try {
    window.doesNotExist();
} catch(error) {
    console.log(error); // TypeError: window.doesNotExist is not a function
}
// Keeps going instead of stopping there because of the error

Example using try catch blocks

function makePizza(toppings) {
  // Short way of returning the new Promise object
  return new Promise(function (resolve, reject) {
    //Reject
    if (toppings.includes("pineapple")) {
      reject("No pineapple on pizza!");
    }
    const toppingTime = 500 + toppings.length * 200;
    //Resolve
    setTimeout(() => {
      resolve(`Here's your pizza with the toppings: ${toppings.join(", ")}`);
    }, toppingTime);
  });
}

async function go() {
  try {
    const pizza2 = await makePizza(["pepperoni", "cheese"]);
    console.log(pizza2); // Here's your pizza...
    // This line will throw an error since we are adding pineapple
    const pizza = await makePizza(["pineapple", "cheese"]);
    // Jumps to the catch block
    const anything = 10; // Is never reached
  } catch (error) {
    console.log(error);
  }
}

go();

.catch() can also be used similar to the previous catch() we saw, by defining a function to handle the error

// makePizza function same as above

function handleError(error) {
  console.log(error);
}

async function go() {
    const pizza2 = await makePizza(["pepperoni", "cheese"]);
    console.log(pizza2); // Here's your pizza...
    const pizza = await makePizza(["pineapple", "cheese"]);
}

go().catch(handleError);

Higher Order function

It's a function that returns another function.

In error handling, can be used to create a function to handle the errors in a simplier way, when having to work with many functions that will throw errors

// makePizza function same as above

function handleError(error) {
  console.log(error);
}

async function go() {
  try {
    const pizza2 = await makePizza(["pepperoni", "cheese"]);
    console.log(pizza2); // Here's your pizza...
    const pizza = await makePizza(["pineapple", "cheese"]);
  } 
  catch (err) {
    console.log(err);
  }
}

function makeSafe(fn, errorHandler) {
  return function () {
    fn().catch(errorHandler);
  };
}

const saferFn = makeSafe(go, handleError);
saferFn();

Ajax and APIs

  • Ajax - Asynchronous JavaScript And XML

When people say Ajax usually they mean fetch data from an API

  • JSON - JavaScript Object Notation

A way to transport JavaScript objects from server to server or server to client.

.json file:

{
"employees":[
  {"firstName":"John", "lastName":"Doe"},
  {"firstName":"Anna", "lastName":"Smith"},
  {"firstName":"Peter", "lastName":"Jones"}
]
} 

When we get a JSON on our JavaScript, we get it as a string, to make it a real object we need to save it in a variable and then parse it into JSON

const text = `{
"employees":[
  {"firstName":"John", "lastName":"Doe"},
  {"firstName":"Anna", "lastName":"Smith"},
  {"firstName":"Peter", "lastName":"Jones"}
]
}` 
// Saves those 3 objects as an array called employees
const obj = JSON.parse(text);
// Logs the first element of the array employees
console.log(obj.employees[0]);
  • Fetch

Allows to do HTTP requests, it does not need to be from a JSON, it could be data from the text of a web page, etc. Fetch is promise-based

Steps to use fetch
  1. Making a Request: You start by using fetch to make a request to a URL. This returns a promise that will eventually resolve to a Response object.

  2. Handling Response: The first then method is used to handle the Response object once it's available. The Response object does not directly contain the actual body of the response; it has a method like .json() or .text() to handle the data format appropriately. This first then checks if the response is OK (status in the range 200-299). If not, it throws an error.

  3. Using the Data: The result from the first then (e.g., the JSON data) is passed to another then method where you can use this data.

  4. Error Handling: If there is an error in any part of the fetch process, such as a network error or if the response status indicates an error, you can catch these errors using a catch block.

To understand better .then after the fetch, here's an example

fetch('https://pokeapi.co/api/v2/pokemon') // 1 - We make a request to a URL
  .then(response => response.json()) // 2 - Promise is resolved, when getting a response we transform it to a specific format
  .then(data => console.log(data)) // 3 - Data is shown in the console
  .catch(err => console.log(err)); // In case of an error, it'll be catched

And to access diferent properties...

First we make a request using fetch on the url, then we get the response, then we are able to manage our data and access to the different properties, then we use a catch

const pkmAPI = `https://pokeapi.co/api/v2/pokemon`;
const pkmParagraph = document.querySelector(".someParagraph");

fetch(pkmAPI)
  .then((response) => response.json())
  .then((data) => {
    const firstPkm = data.results[0]; // Taking the first array item
    console.log(firstPkm); // {name: 'bulbasaur', url: 'http://asdasd'}
    const name = firstPkm.name;
    console.log(name); // 'bulbasaur'
    console.log(firstPkm.url); // Url

    const fourthPkm = data.results[3];
    console.log(fourthPkm.name); // 'charmander'

    // Add a property from the API to our DOM
    pkmParagraph.textContent = `${fourthPkm.name}`;
  })
  .catch((err) => console.log(err));

Using async await we can skip then

const pkmAPI = `https://pokeapi.co/api/v2/pokemon`;

async function displayPkm() {
  const response = await fetch(pkmAPI); // Making a request
  const data = await response.json(); // Handling the response
  console.log(data.results[0].name); // Using data. Logging 'bulbasaur'
}

displayPkm().catch(err => console.log(err));

Explaining urls

In order to understand APIs better and be able to manipulate different sections of a URL when using an API, we need to understand better how URLs work

A URL consists of ten parts: the scheme, subdomain, top-level domain, second-level domain, subdirectory, parameter, port, path, query and fragment, It does not always have all of them, but at least contains three of them

https://blog.hubspot.com/hubfs/URL%20Parts_300-02.jpg

parts of a URL graphic guide

1. Scheme

https://

Tells web servers which protocol to use when it accesses a page on your website

The most common protocol is HTTPS .

2. Subdomain

https://studio

Indicates which particular page of your website the web browser is serving up

3. Second-level domain

https://studio.youtube

The name of your website

4. Top-level domain

https://studio.youtube.com

Specifies what type of entity your organization registers as on the internet

.com is intended for commercial entities in the United States, or .edu intended for academic institutions

5. Subdirectory

https://shop.yourstore.com/ hats

Refers to which particular section of a webpage you are on

Note: The subdomain is shop, and the subdirectory is "hats", that means this URL would serve up the "Hats" page, which is a subfolder of the "Shop" page. Where you should also be able to find Shirts, pants, etc

6. Port

htttps://blog.hubstop.com:443/marketing

Specify a connection endpoint to direct data or specific service. It's associated with a host network address. The default port is 443

7. Path

https://blog.hubspot.com/marketing /parts-url

Specifies the location of the file or resource the user is accessing.

8. Query

https://www.youtube.com/results ? search_query=hello

A question mark that says a specific query is being performed and is used to procede a query string. A query string specifies the parameters of the data being asked from a website's database

9. Parameters

A Beginner's Guide to URL Parameters

Paremeters are the values being queried during a search. The parameter can be a number, encrypted value, search term or another data that can be found on the website. URL parameters contain a key and a value, separated by the equal sign =

A URL can contain multiple parameters, which are separated by ampersand &

10. Fragments

Optional component of URLs, tipically placed at the end with a hash #. Indicates a specific location on the webpage like an ID or name attribute


CORS

Cross Origin Resource Sharing

HTTP-header based mechanism that allows a server to indicate any origins (domaing, scheme or port) other than its own from which a browser should permit loading resourse.

For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. For example, fetch() and XMLHttpRequest follow the same-origin policy. This means that a web application using those APIs can only request resources from the same origin the application was loaded from unless the response from other origins includes the right CORS headers.

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/cors_principle.png

Proxy

CORS can send an error when using fetch depending on the rules the server has, we can work with this by using a proxy

domainName.com - - - - - - somethingAPI.com // Can give an error

domainName.com - - - - Proxy - - - - somethingAPI.com

The API will check if the proxy is viable, then the proxy sends it to us

const pkmAPI = `https://pokeapi.co/api/v2/pokemon`;
const proxy = `https://cors-anywhere.herokuapp.com/`;

async function handleRecipe(query) {
  const response = await fetch(`${proxy}${BASE}?search=${query}`);
  const data = await response.json();
  console.log(data);
}

Modules

Way to structure and organize your JavaScript and has the abilty to share functionality and data across multiple files or projects

  • Modules have their own scope

To use modules we need to add the type module to our script tag inside our html

<body>

  <script src="script.js" type="module"><script>
</body>
  • Export

To use a functionality of a file inside of another file we need to export it first.

Two ways to export things, we can do it on the same line or at the bottom, which are called named exports

// File 1
export const something = "Kep1er"; // Exporting on the same line
const canadianTaxRate = "Not really...";
const howAreU = 'how are you?'

export function sayHi(name) { // Exporting on the same line
  return `hi ${name}, ${howAreU}`;
}

function calculateTaxRate() {
  const can = 42;
  return can * 0.21;
}

// Exporting at the bottom of the file - Named exports
export { canadianTaxRate, calculateTaxRate };
  • Import

To use it we need to import it inside the file we need it

Always do the imports on the top of the file

// File 2
import { calculateTaxRate, canadianTaxRate, sayHi } from "./taxRate.js";


const name = 'John Doe'
console.log(sayHi(name)); // Hi John Doe, how are you?
console.log(calculateTaxRate()); // 8.82
console.log(canadianTaxRate); // "Not really..."

The modules having their own scope means that using sayHi, we can even use it when it includes the variable howAreU, which was declared on the other file

  • Default

If we want to export a single value or to have a fallback value for your module, you could use a default export:

// module "my-module.js"

export default function cube(x) {
    return x * x * x;
  }
// Index js file
import cube from "./my-module.js";

console.log(cube(3)); // 27
  • Everything

// something.js
export const name = 'hi';
export const hello = 'hello';
export const isTrue = true;
export function sayHi() {
    return 'hello'
}
// main.js
import * as everything from './something.js'
// Named as everything
console.log(name); // hi

Import on demand

If you only need to import something when you need it instead of when the script loads, you can use a different way

// currencies.js 
const currencies = {
    USD: 'United States Dollar',
    AUD: 'Australian dolar',
}
export default currencies;

Import when using a button

// other-file.js
async function button(){
    const currenciesModule = await import('./currencies.js')
}

Destructuring

Used to shorten our code. When we use an object or an array, to access their properties we had to do something like this

  • Object destructuring
const person = {
  name: "John",
  age: 91,
}
// To access this values we would need to write the following:
const name = person.name;
const age = person.age;

We can simplify to this, by adding curly brackets on the left of the equal, we have to use the field names defined in the object

const {name, age } = {
  name: "John";
  age: 91,
} // name = John, age = 91
  • Array destructuring
const user = ["John", "Doe"];
// Accessing the array index:
const firstName = user[0];
const lastName = user[1];

Instead we can move those variables to the left side of the equal using square brackets, the name can be whatever we can, the order is important. The first variable will take the value of the first index in the array

const [firstName, lastName] = ["John", "Doe"]; 
// firstName = John, lastName = Doe

;

Databases

Are a structured collection of data that allows efficient retrieval, insertion, updating and deletion of data.

There are two types of databases. Relation Databases (RDBMS) and NoSQL Databases. In this document we will mostly focus on SQL, RDBMS and PostgreSQL

NoSQL Database:

Designed for specific data models and have flexible schemas. Examples include:

  • Key-Value Stores: Redis, DynamoDB

  • Column Stores: Cassandra, HBase

  • Graph Databases: Neo4j, Amazon Neptune

    Document Stores: MongoDB, CouchDB

    NoSQL reference image

Relational Database (RDBMS)

Stores data as collections of tables. Relations are defined between tables for cross referencing.

The way it stores data makes users easy to understand the structure and content of the data. Developers may use Structured Query Language (SQL) to query data, and add indexes to database for faster querying.

Examples of SQL Databases: MySQL, PostgreSQL, Oracle.

Table

A relational database consists of a collection of tables (i.e. entities), from which we want to seek information. A table consists of columns, which are the properties of the table, and rows which are the records to store and retrieve.

Column

Columns refer to a set of fields in tables. A column describes a property we are interested in storing for the table it belongs to.

Relationship

A relationship is a connection between two entities. It connects data (in tables) together in meaningful ways. For instance, knowing the information of a transaction is meaningless without knowing the customer who performed the transaction so those tables are connected between

Employees Table

| EmployeeID | FirstName | LastName | Position | Department | Salary | HireDate | | ---------- | --------- | -------- | ----------------- | ---------- | ------ | ---------- | | 1 | John | Doe | Software Engineer | IT | 80000 | 2020-01-15 | | 2 | Jane | Smith | Data Analyst | IT | 75000 | 2019-06-01 | | 3 | Emily | Johnson | Product Manager | Marketing | 90000 | 2018-11-23 | | 4 | Michael | Brown | HR Specialist | HR | 60000 | 2021-03-10 | | 5 | Sarah | Davis | Financial Analyst | Finance | 70000 | 2017-08-14 |


Before going into SQL, some important concepts are:

What's the use of a database?

Databases are used for several things, such as storing customer data, transaction records, inventory, storing user information, content, session data, etc!

Where are databases stored?

They can be stored locally, or you can use a server.

When using servers they can be:

  • On-Prem/Company Server: Hosted internally and company full control of the server

  • Cloud Server/Serverless: Companies like AWS, Google Cloud and Azure. For a price they will manage many things

What is a query?

A query is a request for data from a database, allowing you to perform various operations:

  • Operations: Inclues creating, reading, updating, and deleting data (CRUD)

Fact Tables

  • Store numerical data (measures) that are subject to analysis, such as sales revenue, quantity sold, or profit.

| SalesID | DateKey | ProductKey | CustomerKey | StoreKey | Quantity | SalesAmount | | ------- | -------- | ---------- | ----------- | -------- | -------- | ----------- | | 1 | 20230101 | 101 | 1001 | 10 | 5 | 500.00 | | 2 | 20230101 | 102 | 1002 | 10 | 2 | 200.00 | | 3 | 20230102 | 101 | 1003 | 11 | 3 | 300.00 |

Dimension tables

  • Describe attributes or dimensions of the data, support filtering, grouping and labeling. Usually less rows of data but more descriptive

| ProductKey | ProductName | Category | Price | | ---------- | ----------- | -------- | ------ | | 101 | Widget A | Widgets | 100.00 | | 102 | Widget B | Widgets | 50.00 | | 103 | Gizmo | Gizmos | 200.00 |


SQL

Structured Query Language is used for managing and manipulating relational databases. Used to perform various operations on the data stored in databases, such as querying, updating, inserting and deleting data. As well as managing database structures like tables, indexes and views

Databases we'll use to work with SQL:

SQLite SQLiteviz

  • Lightweight, file-based database

  • Ideal for small to medium applications

  • Zero configuration; easy setup

SQLite works on the web, in the link you can find the workspace where you can start creating tables and practicing as soon as you are there. Click the link above

PostgreSQL

Installation and use will be explained in detail later.

  • Advanced, open-source relational database

  • Suited for large applications

  • Supports complex queries, transactions

Basic Keywords

This are the keywords to work with SQL, from creating tables, to selecting elements and managing all your data.

Keywords are not case sensitive, so they can be lowercase if necessary, but using capital letters will make a better readability

(Just for testing, go into sqliteviz workspace and let's start practicing this keywords)

  • CREATE TABLE - Defines a new table structure in a database. Also defines the columns and the data types

    CREATE TABLE employee_table(
        employee_id INTEGER,
        name TEXT
    );
    -- Integer and Text are types of data
    
  • INSERT INTO - Add new records (rows) into an existing table in the db, following the types

    INSERT INTO employee_table VALUES
    (1, 'John Doe'),
    (2, 'Hebs'),
    (3, 'Samuel Oak');
    
    -- In PostgreSQL specify the columns in order too
    INSERT INTO employee_tables (employee_id, name) VALUES
    (1, 'John Doe'),
    (2, 'Hebs'),
    (3, 'Samuel Oak');
    
  • UPDATE - Modifies existing data in a table

  • SELECT - Identifies the columns (or data) from the database

  • FROM - Identifies the table we are connecting to

  • * - Special command to select all the columns

    SELECT * 
    FROM table_example;
    

    To select only a few columns, remove the * and place your columns separated by commas

    SELECT employee_id, name
    FROM employee_table;
    
  • LIMIT - query only a certain amount of rows

    SELECT * 
    FROM employee_table LIMIT 5;
    -- Will only show the first 5 rows
    
  • DISTINCT - get unique rows. But is a resource intense

    In this case it will show us the UNIQUE rows in the role of the employee list, if there is any duplicated won't be shown

    SELECT DISTINCT employee_role
    FROM employee_table;
    
  • WHERE - sets a condition for the query. Filtiring it out

    This example give sthe results with the name John

    SELECT * 
    FROM employee_table
    WHERE name = 'John';
    
  • ALTER TABLE - Modifies an existent table. Can have multiple subcommands

    -- Add a column
    ALTER TABLE employee_table 
    ADD COLUMN new_column_hello TEXT;
    
    -- Remove a column
    ALTER TABLE exmployee_table
    DROP COLUMN employee_id;
    
    -- Rename a columnn
    ALTER TABLE employee_table
    RENAME COLUMN employee_id TO new_name_id;
    
    -- Change type. (Serial is an auto increased number)
    ALTER TABLE employee_table
    ALTER COLUMN employee_id TYPE SERIAL;
    
    -- Set default column
    ALTER TABLE employee_table
    ALTER COLUMN employee_id SET DEFAULT 0;
    
    -- Drop default
    ALTER COLUMN employee_id DROP DEFAULT;
    
  • UPDATE - Used to modify existing rows in a table.

  • SET - Specifies the column to be updated and the new value for that column

    UPDATE table_name
    SET column_name = new_value,
        column_name2 = new_value2
    WHERE condition;
    

    Allow me a pause to give an example of this update keyword since it's gonna be used a lot, we should get familiar with it. Let's say we have this table and want to update some emails

    | id | name | email | department | | --- | ------- | ------------------- | ---------- | | 1 | Alice | [email protected] | Sales | | 2 | Bob | [email protected] | Marketing | | 3 | Charlie | [email protected] | Sales |

    We want to change Bob's email, he also joined Sales department for some reason! So we need to update the select wich the column and row of the data we need to change

    UPDATE department_emails
    SET email = '[email protected]',
        department = 'Sales'
    WHERE id = 2;
    
  • Should be changed like this:

    | id | name | email | department | | --- | ------- | ---------------------- | ---------- | | 1 | Alice | [email protected] | Sales | | 2 | Bob | [email protected] | Sales | | 3 | Charlie | [email protected] | Sales |

  • -- Comments, used to document the query. /*Hi*/ is used for multiline

  • ORDER BY - Specify a column and orders by the value, from low to hight. You can specify DESC to make it descending order

    SELECT *
    FROM employee_table
    ORDER BY employee_id DESC;
    
  • Null values

    A field with no value, different from 0 or ""-

    WHERE salary_year_avg IS NULL;
    
  • DROP TABLE - Deletes a table, be extremely careful since this can delete your whole database table ⚠️⚠️⚠️

    DROP TABLE employee_table
    
For fast information about keywords:

alt text

SQL Keywords IMG - Credits to Sahn Lam and ByteByteGo for their great video on this topic: Roadmap for Learning SQL - YouTube

  1. Data Definition Language (DDL):

    • CREATE: Used to create database objects like tables, indexes, views, and databases themselves.
    • ALTER: Modifies the structure of existing database objects.
    • DROP: Deletes database objects like tables, indexes, or databases.
    • TRUNCATE: Removes all records from a table, but keeps the table structure intact.
  2. Data Manipulation Language (DML):

    • SELECT: Retrieves data from a database.
    • INSERT: Adds new records to a table.
    • UPDATE: Modifies existing records in a table.
    • DELETE: Removes records from a table.
  3. Data Control Language (DCL):

    • GRANT: Provides user access privileges to database objects.
    • REVOKE: Removes user access privileges granted with the GRANT command.
  4. Transaction Control Language (TCL):

    • COMMIT: Saves all changes made since the last COMMIT or ROLLBACK statement.
    • ROLLBACK: Reverts any changes made since the last COMMIT or ROLLBACK statement.
    • SAVEPOINT: Sets a point in the transaction to which you can later roll back.
  5. Constraints:

    • PRIMARY KEY: Enforces uniqueness of a column or set of columns (also implicitly creates a unique index).
    • FOREIGN KEY: Establishes a link between data in two tables, enforcing referential integrity.
    • UNIQUE: Ensures that all values in a column are unique.
    • CHECK: Specifies a condition that must be met for a row to be inserted or updated.
  6. Other Important Keywords:

    • FROM: Specifies the table from which data is retrieved in a SELECT statement.
    • WHERE: Filters records based on specified conditions in a SELECT, UPDATE, or DELETE statement.
    • ORDER BY: Sorts the result set in ascending or descending order based on one or more columns.
    • GROUP BY: Groups rows that have the same values into summary rows.
    • HAVING: Filters groups returned by a GROUP BY clause.
    • JOIN: Combines rows from two or more tables based on a related column between them.

Data Types

Understanding data types is crucial for designing and managing databases effectively.

Numeric Data Types

  • INT - Integer Values, commonly used for whole numbers

  • FLOAT / REAL - Floating-point numbers for approximate numeric values

  • DOUBLE - Double-precision floating numbers for more precision

  • DECIMAL / NUMERIC - Exact numeric values with specified precision

Character Data Types

  • CHAR(n) - Fixed lenght character string

  • VARCHAR(n) - Variable length character string with maximum of n

  • TEXT - Varialbe length character string with no specific limit

Date and Time Data Types

  • DATE - Date values (year, month, day)

  • TIME - Time values (hour, minute, second)

  • DATETIME / TIMESTAMP - Date and time values combined

  • INTERVAL - Represents a time span (6 days, 1 hour)

Boolean Data Types

  • Boolean - Represents TRUE, FALSE, NULL

Binary Data Types

  • Binary - Fixed-length binary data

  • VARBINARY - Variable-length binary data

Comparisions

Used within the WHERE or HAVING keywords. Used in conjuction with comparison operators:

  • =, <>, >, <, >= and <=

  • AND, OR, BETWEEN, and IN

For example to filter the data that's equal to a condition, we can use <>, = or NOT

-- NOT equal to 'Data Analyst'
SELECT * 
FROM employee_table
WHERE job_role <> 'Data Analyst'

For numbers we can use the numeric ones, <, >, <=, >=

-- Select from where salary is greater than 6000
SELECT job_location, job_id, salary_year_avg
FROM job_postings_fact
WHERE salary_year_avg > 6000
ORDER BY salary_year_

-- When salary is less or equal than 6000
WHERE salary_year_avg <= 6000

AND operator is used to get two conditions, you can have multiple AND. OR is for either one of the two conditions

-- More than 100000 while also being data analyst
WHERE salary_year_avg > 100000 AND job_title = 'Data Analyst'
-- More than 100000 or data analyst
WHERE salary_year_avg > 100000 OR job_title = 'Data Analyst'
-- Between two ranges
WHERE salary_year_avg BETWEEN 60000 AND 90000
-- In Specifies multiple values in a where clause
WHERE job_location IN ('Boston, MA', 'Anywhere')

To have more than one comparisions, we can wrap them in ().

In this next example whe want the people who lives in Boston, and two couples of comparisions, to do this we wrap everything in parenthesis to separate it

SELECT 
job_location, 
job_id, 
job_title_short, 
salary_year_avg
FROM job_postings_fact 
WHERE job_location IN ('Boston, MA', 'Anywhere') AND 
( 
  (salary_year_avg > 100000 AND job_title_short = 'Data Analyst') OR 
  (salary_year_avg > 70000 AND job_title_short = 'Business Analyst')
)
ORDER BY salary_year_avgr_avg

Wildcards

Used to substiute one or more characters in a string

They are used with the LIKE operator

LIKE, % and _ are used inside WHERE clause

WHERE job_title LIKE '%Data%'
  • % - Represents zero, one or more characters. Used to query data that contains a specific word or character
-- Query from all the job titles the ones that contain Analyst at any point
SELECT job_id, job_title
FROM job_postings_fact
WHERE job_title LIKE '%Analyst%'

-- Query for the ones that only have analyst as the first word
WHERE job_title LIKE 'Analyst%'

-- Only as last word
WHERE job_title LIKE '%Analyst'
  • _ - Represents one, single character. Used to query two or more words together
WHERE job_title LIKE '%Expert Operations Data%'

AS

AS renames a column in a table, a temporary name (alias).

Makes columns more readable

SELECT
    job_title AS Job,
    job_location AS Location,
    job_via AS Online_platform,
    salary_year_avg AS Salary
FROM job_postings_fact AS jpc
WHERE job_via LIKE '%Linkedin%'

Result of the query:

| Job | Location | Online_platform | Salary | | ------------------------------------------------------------------- | --------------------- | --------------- | ------ | | Data Center Engineer | Sydney NSW, Australia | via LinkedIn | 82500 | | Business Intelligence Analyst | Mountain View, CA | via LinkedIn | NULL | | Data Analyst | Las Vegas, NV | via LinkedIn | 55000 | | Data Analyst (Business) | Oakland, CA | via LinkedIn | 135000 | | Data Analytics Engineer (Transportation, Logistics and Fulfillment) | Sunnyvale, CA | via LinkedIn | 146500 | | Data Engineer | New York, NY | via LinkedIn | 120000 |

Operations

Are used to perform mathematical calculations

+, -, *, /, %

We can place this operations in different locations:

  1. <u>SELECT Clause</u>

    For performing calculations on data retreived from the database. Creates a temporary column that shows you the result of the operation

    SELECT 
      salary,
      salary * 0.1 AS Bonus,
      salary + (salary * 0.1) AS Total
    FROM salary_table
    -- Bonus and Total are temporary, they are not added to the db
    

Example of result

| salary | Bonus | Total | | ------ | ----- |:----- | | 100 | 10 | 110 | | 2000 | 200 | 2200 |

  1. <u> WHERE Clause</u>

    For filtering data based on conditions that may involve arithmetic or logical operations

    SELECT * 
    FROM orders
    WHERE (quantity * unit_price) > 100;
    
  2. <u>ORDER BY Clause</u>

    For sorting the results based on expressions involving operations

    SELECT
      name,
      age,
      (current_date - birth_date) / 365 AS age_years
    FROM people
    ORDER BY (current_date - birth_date) / 365
    
  3. <u>GROUP BY and HAVING Clauses</u>

    For grouping rows that have the same values in specified columns and filtering groups with conditions involving operations

    SELECT
      department,
      SUM(salary) AS total_salary
    FROM employees 
    GROUP BY department HAVING SUM(salary) > 100000
    

After running this code, the departments will be grouped in their own section, HAVING will exclude the ones that are less than 100,000 when SUM adds all the salaries of the department. Example of this GROUP BY and HAVING:

Sample Table:

| department | employee_name | salary | | ---------- | ------------- | ------ | | IT | John | 60000 | | IT | Jane | 50000 | | HR | Alice | 70000 | | HR | Bob | 40000 | | Sales | Charlie | 30000 | | Sales | Dave | 20000 |

Process: Grouping by Department and filtering

  • IT: John (60000) + Jane (50000) = 110000

  • HR: Alice (70000) + Bob (40000) = 110000

  • Sales: Charlie (30000) + Dave (20000) = 50000

  • The HAVING clause filters out the Sales department because its total salary (50000) is not greater than 100000.

Returns:

| department | total_salary | | ---------- | ------------ | | IT | 110000 | | HR | 110000 |

Modulo %

Modulo operator is used to get the reminder of dividing something. Also used to see if a number is even or odd

In this case, first we use WHERE for the hours between 8 and 16, since more than 8 hours is considered extra hours.

SELECT 
  activity_id,
  hours_spent,
  hours_spent % 8 AS extra_hours
FROM invoices_fact
WHERE (hours_spent BETWEEN 8 AND 16) AND
extra_hours > 0
ORDER BY hours_spent

Aggregation

Refers to the process of summarizing or combining multiple rows of data into a single result based on a specified criterion. Aggregation functions are commonly used in conjuction with the GROUP BY and HAVING clauses

  • SUM() - Adds together all the values in a specific column

  • COUNT() - Counts the number of rows that match the specified criteria

  • AVG() - Calculates the average value in a specific numeric column

  • MAX() - Finds the maximum value in a set of values

  • MIN() - Finds the minimum value in a set of values

Example Table:

| product | quantity | price | | ------- | -------- | ----- | | A | 10 | 5.00 | | B | 20 | 7.50 | | A | 15 | 5.00 | | B | 10 | 7.50 | | C | 5 | 10.00 |

SUM() deeper example

To find the total quantity sold for each product:

select product, SUM(quantity) AS total_quantity
FROM sales
GROUP BY product;

Result:

| product | total_quantity | | ------- | -------------- | | A | 25 | | B | 30 | | C | 5 |

HAVING

Filter query based on aggregate functions and groupings

Used because you can't aggregate functions in WHERE

Good practice: GROUP BY columns that are not aggregating}

Joins

SQL joins are used to combine ROWS from two or more tables based on a related column between them. The most common ones are: INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN

Syntaxis:

We will use dot notation to access a table and its columns

SELECT a.*, b.*
FROM table_a a
INNER JOIN table_b b
ON a.common_column = b.common_column;

For a better understanding on the topic, let's see our two tables first:

employees

| employee_id | name | department_id | salary | | ----------- | ------- | ------------- | ------ | | 1 | John | 1 | 60000 | | 2 | Jane | 2 | 50000 | | 3 | Alice | 1 | 70000 | | 4 | Bob | 2 | 40000 | | 5 | Diana | 3 | 30000 | | 6 | Charlie | 5 | 80000 |

departments

| id | department_name | | --- | --------------- | | 1 | IT | | 2 | HR | | 3 | Sales | | 4 | Marketing |

employees is our Fact Table, departments is our Dimension Table. They are connected with department_id

We are going to join both tables.

INNER JOIN

Returns only the rows that have matching values in both tables

-- We will show the name column of the employees table, and department_name
SELECT employees.name, departments.department_name
FROM employees
INNER JOIN departments
-- Specify what the connection is of each table
ON employees.department_id = departments.id;

Result of INNER JOIN:

| name | department_name | | ----- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales |

So what happened here?

INNER JOIN only returns the rows where there is a match in both tables, 'Charlie' is not included because there is no matching department_id

LEFT JOIN

Returns ALL of the content of the column from table A, while from B it's only going to return the content that matches

SELECT employees.name, departments.department_name
FROM employees
LEFT JOIN departments
ON employees.department_id = departments.id;

Result of LEFT JOIN :

| name | department_name | | ------- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales | | Charlie | NULL |

LEFT JOIN returns all rows from the left table (employees) and the matched rows from the right table (departments).

Since there is no match, the result NULL is added

We got all rows from our first table, and our second table only sent us what matches

RIGHT JOIN

The opposite to a LEFT JOIN, returns ALL the content of the column from table B, while A only will return what matches

SELECT employees.name, departments.departments_name
FROM employees
RIGHT JOIN departments
ON employees.department_id = departments.id;

Result of RIGHT JOIN:

| name | department_name | | ----- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales | | NULL | Marketing |

No one matches the Marketing department, so we get all the rows from table B, and only the ones that match from A

FULL JOIN

SELECT employees.name, departments.departent_id
FROM employees
FULL JOIN departments
ON employees.department_id = departments.id

Result of FULL JOIN:

| name | department_name | | ------- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales | | Charlie | NULL | | NULL | Marketing |

Order of execution

This order is NOT the order you are placing the clauses, it's the order SQL process the information.

This sequence ensures SQL queries are processed efficiently and logically.

  1. FROM/JOIN

    • Specifies the table to retreive data from and how to join them
  2. WHERE

    • Filters rows based on conditions
  3. GROUP BY

    • Group rows that share properties so aggregate functions can be applied
  4. HAVING

    • Filters groups based on aggregate conditions
  5. SELECT

    • Select specified columns to display
  6. DISTINCT

    • Removes duplicate rows
  7. ORDER BY

    • Sorts the results based on specified columns/values
  8. LIMIT/OFFSET

    • Limits the number of rows returned, often used for pagination

PostgreSQL

An open-source relational database management system. Known for its robustness, extensibility and compliance with SQL standards.

It's used as the backend database for web applications, to handle user data, content and transactions, to store large volumes of data with complex queries and supports business applications that require hight reliability

First step: Installation

Go to: https://www.postgresql.org and install the latest version for your operating system

On the installer, for most things you will keep the deafult, PostgreSQL will ask you to set a password, this is important so write it down somewhere. This password is used to define the superuser. If you change the port number, also write it down

Open Pg Admin

Look for PgAdmin 4, this is the executable that will open PostgreSQL's admin dashboard where you can control it

PostgreSQL in VSCode

VSCode it's our code editor where we will run our SQL queries. To connect our PostgreSQL to our VSCode:

  • Look for SQLTools in the Extensions section inside VScode

  • Install SQLTools PostgreSQL/Cockroack driver

Setup

We should see SQLTools on our left sidebar as a new icon with an option of "Add a new connection"

Select PostgreSQL, this is to connect to our PostgreSQL databases, name the connection, in Database place the name of the database you want to connect, in this case postgres since it's the default, and below on Username also write postgres since that's the default username. Then Test connection, enter the password you set for PostgreSQL Superuser and save

You will see your postgress connection in VS code, but we will not be changing this default database, create another one select the database to run queries on it.

Add a New SQL file on the icon above, you can test by typing:

CREATE DATABASE your_database;

And run your query, by selecting run or highlight your line and press and press Etrl + E, Ctrl + E (twice)

This will create a database that you will be able to see inside Pg Admin, it won't be activated until you click it there

Add another connection in VSCode, name your connection, type your database's name, write your username(postgres), test and save the connection.

To navigate between your connections you will see in the bottom left which database you are currently in.

Let's give an example of creating our first PostgreSQL database to add some notes too:

  • Create the table in your new server and insert the schema of the table using Data Types
CREATE TABLE jobs_example(
    job_id INT,
    job_name VARCHAR(50),
    date_join DATE,
    resume_file TEXT,
    has_experience BOOLEAN
    row_index SERIAL
);

INSERT INTO jobs_example (job_id, job_name, date_join, resume_file, has_experience) VALUES
(1, 'Fullstack Dev', '2024-07-19', 'hebi_resume.pdf', true),
(2, 'Data Analyst', '2024-07-19', 'andy_resume.pdf', true),
(3, 'Finance analyst', '2024-07-19', 'john_resume.pdf', true),
(4, 'Data science', '2021-07-19', 'resume_final.pdf', true),
(5, 'Front end web dev', '2022-07-19', 'hello_resume.pdf', true),
(6, 'Database administrator', '2019-10-19', 'resume_final_FINAL_finaaaal.pdf', true);

SELECT * 
FROM jobs_example

Semicolons are important, they mark that that's the end of the block wich allows you to write all of this and run it together since it's separated with semicolons. It will be taken as separated queries

The table should look like this:

| job_id | job_name | date_join | resume_file | has_experience | row_index | | ------ | ---------------------- | ---------- | ------------------------------- | -------------- | --------- | | 1 | Fullstack Dev | 2024-07-19 | hebi_resume.pdf | true | 1 | | 2 | Data Analyst | 2024-07-19 | andy_resume.pdf | true | 2 | | 3 | Finance analyst | 2024-07-19 | john_resume.pdf | true | 3 | | 4 | Data science | 2021-07-19 | resume_final.pdf | true | 4 | | 5 | Front end web dev | 2022-07-19 | hello_resume.pdf | true | 5 | | 6 | Database administrator | 2019-10-19 | resume_final_FINAL_finaaaal.pdf | true | 6 |

What happened here? If you played a bit with SQLite, you will note some differences. First, the INTEGER is named INT here, then INSERT INTO needs to know which columns are going to receive data and then insert that data. This is important, if you leave something empty a NULL will take its position:

INSERT INTO jobs_example (job_id, job_name) VALUES
(1, 'dentist'),
(2, 'chef')

This will add two rows that will contain information in the columns job_id and job_name, but will give NULL to every other row (except row_index because SERIAL automatically fills a number)

| job_id | job_name | date_join | resume_file | has_experience | | ------ | -------- | --------- | ----------- | -------------- | | 1 | dentist | NULL | NULL | NULL | | 2 | chef | NULL | NULL | NULL |

You can export your table as JSON to access every property and its value

[
  {
    "job_id": 1,
    "job_name": "Fullstack Dev",
    "date_join": "2024-07-1",
    "resume_file": "hebi_resume.pdf",
    "has_experience": true,
    "row_index": 1
  },
  {
    "job_id": 2,
    "job_name": "Data Analyst",
    "date_join": "2024-07-19",
    "resume_file": "andy_resume.pdf",
    "has_experience": true,
    "row_index": 2
  },
]

Constrains

SQL allows you to define constraints on columns and tables. Constraints give you as much control over the data in your tables as you wish. If a user attempts to store data in a column that would violate a constraint, an error is raised. This applies even if the value came from the default value definition.

Check constrain

Specify that the values in a certain column must satisfy a Boolean expression. For example to require positive product prices

CREATE TABLE procuts(
    product_number INT,
    name TEXT,
    price NUMERIC CHECK(price > 0)
);

Not-Null constrain

Specifies that a column must not assume the null value

CREATE TABLE products(
    product_number INT NOT NULL,
    name TEXT NOT NULL,
-- You can have more than one constrain
    price numeric NOT NULL CHECK (price > 0)
);
Tip

In most database designs the majority of columns should be marked not null.

Unique constrains

Ensures that the data contained in a column is unique among all the rows

CREATE TABLE products(
    product_number INT UNIQUE,
    name TEXT
);
-- Check for multiple columns
CREATE TABLE products(
    product_number INT,
    name TEXT,
    something_else TEXT
    UNIQUE (product_number, something_else)
);

Primary Key

Indicates that a column can be used as a unique identifier for rows in a table. The values must be both unique and not null.

Similar to when doing Joins, primary keys and foreign keys can be used to connect two or more tables. Basically it connects a column to another column

  • Declare primary Keys
CREATE TABLE departments(
    department_id INT PRIMARY KEY,
    department_name TEXT NOT NULL
);
-- Primary keys can span more than one column
CREATE TABLE example(
    a INT,
    b INT,
    c INT,
    PRIMARY KEY (a, c)
);

Adding primary keys will automatically create a unique B-tree index on the column and will force to be marked NOT NULL

Foreign Keys

Specifies that the values in a column must match the values appearing in some row of another table

CREATE TABLE products(
    product_number INT PRIMARY KEY,
    name TEXT,
    price NUMERIC
);

CREATE TABLE orders(
    order_id INT PRIMARY KEY,
    product_number INT REFERENCES products(product_number),
    quantity INT
);

-- For multiple columns
CREATE TABLE orders1(
    a INT PRIMARY KEY,
    b INT,
    c INT,
    FOREIGN KEY(a, c) REFERENCES products(c1, c2)
)

Primary&ForeignKeys

ON DELETE

Items can be removed, what if an item was removed while other table was referencing it? SQL allows to handle that. You could:

  • Disallow deleting the referenced product (RESTRICT)

  • Delelte the referenced product as well (CASCADE)

  • Set the referenced product to NULL or Update it (SET NULL)

RESTRICT prevents deletion of the referenced row, NO ACTION it's the default raise an error since the referenced item does not exist.

CASCADE specifies when the referenced row is deleted, the one that's referencing it should automatically be deleted too.

SET NULL and SET DEFAULT sets the row of the table who is referecing to NULL since the referenced does not exist. Instead of null you can also specify it to be the default value

CREATE TABLE tenants(
    tenant_id INT PRIMARY KEY
);

CREATE TABLE users(
    tenand_id INT REFERENCES tenants ON DELETE CASCADE,
    user_id INT NOT NULL,
    PRIMARY KEY (tenant_id, user_id)
);

CREATE TABLE posts(
    tenant_id INT REFERENCES tenants ON DELETE CASCADE,
    post_id INT NOT NULL,
    author_id INT,
    PRIMARY KEY (tenant_id, post_id),
    FOREIGN KEY (tenant_id, author_id) REFERENCES users ON DELETE SET NULL (author_id)
)

Date functions

Used to perform operations on date and time values

  • ::DATE - Converts to a date format by removing the time portion

  • TIMESTAMP / TIMESTAMP WITH TIME ZONE - Data type, specifies the date and time values correctly, can be used with UTC

  • AT TIME ZONE - Converts a timestamp to a specified time zone when selecting

  • EXTRACT - Gets specific date parts (e.g. year, month, day)

First, let's explain ::

:: Is used to parse or "cast" a data type into another, for example, this strings are converted into the correct data type:

SELECT
'123'::INT,
'true'::BOOLEAN,
'2029-12-30':: DATE,
'3.14'::REAL;

As a result:

| int4 | bool | date | float4 | | ---- | ---- | ---------- | ------ | | 123 | true | 2029-12-30 | 3.14 |

You can use it with column names too, let's give a proper example:

CREATE TABLE events(
    id SERIAL,
    name VARCHAR(30) NOT NULL,
    event_date TIMESTAMP
);

INSERT INTO events (name, event_date) VALUES
('Partyyy', '2029-10-12 14:30:00');

SELECT id, 
name, 
event_date::DATE 
FROM events;

/* 
We parse our event_date from TIMESTAMP to DATE, so we get only:
2029-10-12, without the hour
*/

Now how do TIMESTAMP WITH TIME ZONE and AT TIME ZONE work?

We first specify when creating the table the data type, it will be a Timestamp that includes time zone (UTC), also we don't want it to be null.

Then insert our data, this includes the date with the exact hour and the -06 is the UTC

CREATE TABLE events(
    id SERIAL,
    name VARCHAR(30) NOT NULL,
    event_date TIMESTAMP WITH TIME ZONE NOT NULL
);

INSERT INTO events (name, event_date) VALUES
('partyyy', '2029-10-12 14:30:00-06'),
('Birthday!!', '2024-07-24 00:00:00-06');

SELECT id, name, 
event_date AT TIME ZONE 'America/Mexico_City' AS mexico_time, 
event_date AT TIME ZONE 'Asia/Tokyo' AS tokyo_time, 
EXTRACT(MONTH FROM event_date) AS date_month
FROM events
ORDER BY id;

Result:

| id | name | mexico_time | tokyo_time | date_month | | --- | ---------- | ------------------- | ------------------- | ---------- | | 1 | partyyy | 2029-10-12 14:30:00 | 2029-10-13 05:30:00 | 10 | | 2 | Birthday!! | 2024-07-24 00:00:00 | 2024-07-24 15:00:00 | 7 |

Create a table from data of other table

We know that if we use SELECT we retreive data from a table, what if we wanted to specify some data and move it into another? It's easier than it might sound like!

CREATE TABLE january_events AS
SELECT * FROM events
WHERE EXTRACT(MONTH FROM event_date) = 1

Let's imagine we have a big table with lots of events, similar to the table above we extract the month to compare for our WHERE condition, if it's one (because January is 1), get all the results where the month is january and create a table with those results

Case expressions

Is a way to apply conditional logic within your SQL entries

  • CASE - Begins the expression

  • WHEN - Specifies the condition to look at

  • THEN - What to do if the condition is true

  • ELSE - (optional), provides output if none of the condition is true

  • END - Concludes the CASE expression

Syntaxis

SELECT 
    CASE
        WHEN column_name = 'Value1' THEN 'Description of Value1'
        WHEN column_name = 'Value2' THEN 'Description of value2'
        ELSE 'other'
    END AS column_description
FROM table_name

Search CASE Expression

This will create another column named as you specify in the end of your case, that will change the data to the one in the cases

For example:

SELECT 
    job_title, 
    CASE job_title 
        WHEN 'Data Analyst' THEN 'Analysisssss'
        WHEN 'Data Scientist' THEN 'Science big brain'
        ELSE 'Unknown'
    END AS department_name
FROM february_jobs

Would give something like:

| job_title | department_name | | ------------------------- | ----------------- | | Senior Data Engineer | Unknown | | Data Analyst | Analysisssss | | Data Analyst | Analysisssss | | Data Scientist | Science big brain | | Data Scientist | Science big brain | | Data Scientist | Science big brain | | Senior Data Engineer | Unknown | | Software Engineer | Unknown | | Data Scientist | Science big brain | | Machine Learning Engineer | Unknown |

You can then aggregate and count how many are there

This can also be used to clarify the status of something:

SELECT order_id,
       customer_id,
       order_date,
       status,
       CASE
           WHEN status = 'P' THEN 'Pending'
           WHEN status = 'C' THEN 'Completed'
           WHEN status = 'S' THEN 'Shipped'
           WHEN status = 'R' THEN 'Returned'
           ELSE 'Unknown'
       END AS status_description
FROM orders;

Subqueries and CTEs

Subqueries/Nested queries and Common Table Expressions(CTEs) are used for organizing and simplifying complex queries by using the result of one query as input to another.

They are used when you want to perform a calculation before the main query can complete its calculation, they are useful in several scenarios for example filtering on aggregated values or calculations

Subquerie example

SELECT name, salary
FROM employees
WHERE salary = (
    SELECT MAX(salary)
    FROM employees
    WHERE department_id = 1
);

CTEs

Common Table Expressions are a way to write complex queries breaking them down into simpler, reusable components. It's defined using WITH

It's a Temporary result that you can reference, only exists during the execution of a query.

WITH numbered_rows AS(
    SELECT job_id
    FROM jobs
    ORDER BY job_id
)
Delete from jobs
Where job_id > 0;

UNION

Union combines the results set of two or more SELECT statements (only distinct values)

SELECT city FROM customers
UNION
SELECT city FROM suppliers
ORDER BY city;

UNION ALL

The UNION ALL command combines the result set of two or more SELECT statements (allows duplicate values).

SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers
ORDER BY city;
;