Destructuring Arrays :

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },
};

/* ------------------------------------------------------ */

// Normally you would do something like this to retrieve elements from an array:
const myArray = [1, 2, 3];
const a = myArray[0];
const b = myArray[1];
const c = myArray[2];

// But starting from ES6, you can do this in single line like:
// This means you are declaring 3 variables of type const named 'x', 'y', 'z'.
// This means you cannot later reassign x, y, or z to different values.
const [x, y, z] = myArray;
console.log(a, b, c); // Prints: 1 2 3

// It is not necessary to extract the entire array into variables. Only a part of the array can be destructured.
const [first, second] = restaurant.categories;
console.log(first, second); // Prints: Italian Pizzeria

// So what if we wanted the first and the third elements instead? We just leave a space for the second element.
let [main, , secondary] = restaurant.categories;
console.log(main, secondary); // Prints: Italian Vegeterian

// if we want to switch main and secodnary then we'd do it like this :
const temp = main;
main = secondary;
secondary = temp;
console.log(main, secondary); //Prints: Vegeterian Italian
// But now you can do just this:
// So what we are doing here is basically
// First we are creating a new array by using the [] operator containing the two elements main and secondary
// Then we are destructuring the array and storing the two elements into the swapped variables.
// We are not using let or const here because we are simple reassigning the values associated with the two variables.
[main, secondary] = [secondary, main];
console.log(main, secondary);
/* ------------------------------------------------------ */

console.log(restaurant.order(2, 0)); //Prints: [ 'Garlic Bread', 'Pizza' ]
const [starter, mainCourse] = restaurant.order(2, 0);
console.log(starter, mainCourse); // Pritns : Garlic Bread Pizza

// [nested array] :
const nested = [2, 4, [5, 6]];
let [i, , j] = nested;
console.log(i, j); // Prints: 2 [ 5, 6 ]
// destructuring the inner array
const [k, , [l, m]] = nested;
console.log(k, l, m); // Prints: 2 5 6

// [Default values]
const [p, q, r] = [8, 9];
console.log(p, q, r); // Prints: 8 9 undefined
// ### setting default values
const [p1 = 1, q1 = 1, r1 = 1] = [8, 9]; //notice how const is present here
console.log(p1, q1, r1); // Prints: 8 9 undefined

Destructuring an object :

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  // we can actually do destructuring of an object rightaway and we can also set default values as well:
  orderDelivery: function ({starterIndex = 1, mainIndex = 0, time = '20:00', address})
  {
    // we now have 4 variable names instead of an object
    console.log(`Order received: ${this.starterMenu[starterIndex]}`); // Prints: Order received: Garlic Bread
  },

};

// so here we are calling a function and passing an object in it
restaurant.orderDelivery({
  time: '22:30',
  address: 'something',
  mainIndex: 2,
  starterIndex: 2,
})

/* ---------------- Destructuring Objects --------------- */

// The variables names should exactly match the property names that we want to retrieve from the object
// Unlike destructuring in arrays, we do not need to specify all the properties
// and the properties can also be specified out-of-order.
// Just like arrays, this now creates 3 new variables
const { name, openingHours, categories } = restaurant;
console.log(name, openingHours, categories);
/* Prints: 
Classico Italiano {thu: {…}, fri: {…}, sat: {…}} 
(4)['Italian', 'Pizzeria', 'Vegetarian', 'Organic'] */

// We can also change the variable names to be different from the property names
const {name: restaurantName, openingHours: workingHours, categories: tags } = restaurant;
console.log(restaurantName, workingHours, tags);

// Using Default Values

// We can also have default values in case the property that we are trying to read does not exist on an object
// The point we are trying to make here is that:
// If a property does not exist on an object, then we would get 'undefined' as is the case when defining a variable named 'menu'
// However, if a property is not defined, and we have assigned a default value to it, the variable will take the default value instead
// as is the case with the variable 'originalMenu'
// 'starterMenu' is a valid field on the object, and we are changing it's variable name to 'starters'. Hence the default value will not be taken.
const { menu, originalMenu = [], starterMenu: starters = [] } = restaurant;
console.log(menu, originalMenu, starters); // Prints: undefined, [], ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad']

/* Mutating Variables */
let a = 111;
let b = 999;
const obj = { a: 23, b: 7, c: 14 };
console.log(a); // Prints: 111
console.log(obj.a); // Prints: 23

// Now if we try to mutate the variables a and b, we will get an error because if we start a line with "{" then java script expects a code block so the trick is to wrap it in parenthesis "()"
// {a, b} = myObject;
// Hence, instead, we have to do this:
({ a, b } = obj);
console.log(a, b); // Prints: 23 7

/* nested objects */
const { fri } = openingHours;
console.log(fri); // Prints: { open: 11, close: 23 }
const { fri: {open: o, close}} = openingHours;
console.log(o, close); // Prints: 11 23

Spread Operator (...) :

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPasta : function (ing1, ing2, ing3)
  {
    console.log(`Here is your pasta with ${ing1}, ${ing2}, ${ing3}.`);
  },
};

/* ------------------------------------------------------ */
/*                     spread operator                    */
/* ------------------------------------------------------ */

/* ------------------------------------------------------ */
// Suppose we have an existing array, and using that we want to create a new array with some other as well.
const arr = [7, 8, 9];
const newArr = [1, 2, ...arr];
console.log(newArr); // Prints: [ 1, 2, 7, 8, 9 ]

// if we wrote const newArr = [1, 2, arr]; then we would have gotten a nested array
//basically we use spread operator when we'd otherwise write each element of the array separated by a comma.


console.log(...newArr); // Prints: 1 2 7 8 9 (this not an array but rather elements of the array)
//whereas,
// console.log(newArr); will log the 'array' 
/* ------------------------------------------------------ */

const newMenu = [...restaurant.mainMenu, 'Gnocci']
console.log(newMenu); // Prints: [ 'Pizza', 'Pasta', 'Risotto', 'Gnocci' ]

// spread operator is kind of similar to destructuring as it helps us in taking elements out of an array. The big difference is that with spread operator we don't have to create new variables.
//As a consequence we can only use it in places where we'd otherwise write values separated by commas.

//copy array
const mainMenuCopy = [...restaurant.mainMenu]; //this is similar to Object.assign

// Join 2 Arrays
const menu = [...restaurant.starterMenu, ...restaurant.mainMenu];

// spread operator works on all so called iterables and not only arrays
// iterables e.g. : arrays, strings, sets, maps
// objects are not iterables

/* ------------- string and spread operator ------------- */
const str = 'Jonas';
const letters = [...str, ' ', 'S.'] // Prints: ['J', 'o', 'n', 'a', 's', ' ', 'S.']
console.log(letters);
console.log(...str); // Prints: J o n a s
/* console.log(`${...str}`)); */ // Prints: Error // this happens as template literal is not a place which expects multiple values separated by commas.
/* ------------------------------------------------------ */

/* ----------- ingredients - spread op example ---------- */
// const ingredients = [prompt('Let\'s make pasta!\nIngredient ! ?'), prompt('Ingredient 2?'), prompt('Ingredient 3?')];
// or
const ingredients = ['mushrooms', 'toilet cleaner', 'ratpoison']

console.log(ingredients); // Prints: [ 'mushrooms', 'toilet cleaner', 'ratpoison' ]

// in the old way we'd pass arguments like this : 
restaurant.orderPasta(ingredients[0], ingredients[1], ingredients[2]);
// using spread operator we'd pass arguments like this : 
restaurant.orderPasta(...ingredients);

// both the above lines will Print:  Here is your pasta with mushrooms, toilet cleaner, ratpoison.
/* ------------------------------------------------------ */
// since es 2018 spread op also works on objects : 
 

/* ---------------- spread op on objects ---------------- */
const newRestaurant = {cofounder: 'dude1', ...restaurant, founder: 'dude2'};
console.log(newRestaurant);
// this creates a new object

// to create a copy of an object : 
const restaurantCopy = {...restaurant};
restaurantCopy.name = 'Rasoi';
console.log(restaurantCopy.name); // Prints: Rasoi
console.log(restaurant.name); // Prints: Classico Italiano

Rest Patterns and Parameters :

  • The Rest pattern is, in a way, the 'opposite' of the spread operator. It uses the same operator, but to condense multiple values into an array.
'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza : function (mainIngredient, ...otherIngredients)
  {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

/* ------------------------------------------------------ */
/*               rest pattern and parameters              */
/* ------------------------------------------------------ */
/* ------------------ 1.) Destructuring ----------------- */
// SPREAD, cuz on RIGHT side of =
const arr = [1, 2, ...[3, 4]];

// REST, because on LEFT side of =
const [a, b, ...others] = [1, 2, 3, 4, 5];
console.log(a, b, others); // Prints: 1 2 [ 3, 4, 5 ]

// using spread and res both : 
const [pizza, , risotto, ...otherFood] = [...restaurant.mainMenu, ...restaurant.starterMenu];
console.log(pizza, risotto, otherFood); // Prints: Pizza Risotto [ 'Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad' ]

// REST must be last element, so we can't do this : 
/* const [pizza, , risotto, ...otherFood, bread] = [...restaurant.mainMenu, ...restaurant.starterMenu]; */
// this will give an error as we have 'bread' afer a rest element

// aslo, there can only be one REST operator in a destructuring assignment

/* ------------------------------------------------------ */

/* ------------- rest operator with objects ------------- */
// the difference is that the remaining objects will be collected into a new object and not into a new array

const {sat, ...weekdays} = restaurant.openingHours;
console.log(weekdays); // Prints: {thu: {…}, fri: {…}}

/* ------------------------------------------------------ */

/* -------------------- 2.) Functions ------------------- */
const add = function (...numbers) {
  /* console.log (numbers); */ // Prints: [ 5, 3, 7, 2 ] // which is an array
  let sum = 0;
  for (let i =0; i<numbers.length; i++) 
    sum += numbers[i];
  console.log(sum);
}
add (2, 3); // Prints: 5
add(5,3,7,2); // Prints: 17

//using an array to call the above function
const x = [2,3,5,4]; // how would we call the add function with an array dawg 😭😭?? By using a spread operator 😈
add(...x); // Prints: 14

/* ------------------------------------------------------ */

restaurant.orderPizza('mushrooms', 'onion', 'olives', 'spinach'); // Prints: mushrooms, [ 'onion', 'olives', 'spinach' ]
restaurant.orderPizza('mushrooms'); // Prints: mushrooms, [] 

Short Circuiting :

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPasta : function (ing1, ing2, ing3)
  {
    console.log(`Here is your pasta with ${ing1}, ${ing2}, ${ing3}.`);
  },
};

/* ------------------------------------------------------ */
/*                    short circuiting                    */
/* ------------------------------------------------------ */
// Logical Operators can:
// Use ANY data type, return ANY data type, short-circuiting
/* ------------------------------------------------------ */

/* ------------------------- OR ------------------------- */
console.log('---- OR ----');

// if the first operand is truthy then it will be evaulated and the remaining operands will not even be checked.
console.log(3 || 'Jonas'); // Prints: 3

console.log('' || 'Jonas'); // Prints: Jonas // as '' is a falsey vaule
console.log(true || 0); // Prints: true
console.log(undefined || null); // Prints: null // here both are falsey values but since null comes after undefined (falsey value) so the output is null.
/* ------------------------------------------------------ */
const guests1 = restaurant.numGuests ? restaurant.numGuests : 10;
console.log(guests1); // Prints: 10 // as restaurant.numGuests doesn't exist

restaurant.numGuests = 23;
const guest2 = restaurant.numGuests || 10;
console.log(guest2); // Prints: 23
// here if instead of '23', '0' was there then we would have gotten '10' as the output. We'll learn how to fix this problem later in the chapter. 

/* ------------------------- AND ------------------------ */
console.log('---- AND ----');

// In case of the && operator, it will return the first falsy value that it encounters.
// If all the values are truthy, then it will return the last value, even if the last value is truthy
console.log(0 && 'Alice'); // Prints: 0
console.log('Alice' && 'Bob'); // Prints: 'Bob'
console.log(23 && 'Alice' && true && null && 'Bob'); // Prints: null


// A practical application of this behavior is as follows:
// Suppose we are trying to call a method on the restaurant object. But we are not sure of the method exists.
// We can wrap the method inside an if-block
if (restaurant.orderPizza) {
  restaurant.orderPizza('mushrooms', 'spinach');
}

// But now, with the AND operator we can do this:
// If the orderPizza property is falsy, the method is never called
restaurant.orderPizza && restaurant.orderPizza('mushrooms', 'spinach');
// But now if we add the property
restaurant.orderPizza = function () {
  console.log('order pizza plzz');
};
restaurant.orderPizza && restaurant.orderPizza(); // // Prints: order pizza plzz

/* ------------------------------------------------------ */ 

Nullish Coalescing Operator () :

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza : function (mainIngredient, ...otherIngredients)
  {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

/* ------------------------------------------------------ */
/*               Nullish Coalescing Operator              */
/* ------------------------------------------------------ */

// if we take restaurant.numGuests = 0;
//then, JS will take it as a null value 
//this is where Nullish Coalescing Operator comes in
// it was introduced in es 2020
restaurant.numGuests = 0;
const guests = restaurant.numGuests || 10;
console.log(guests); // Prints: 10

// using the Nullish Coalescing Operator
const guestsCorrect = restaurant.numGuests ?? 10;
console.log(guestsCorrect); // Prints: 0

// this happens because Nullish Coalescing Operator works with the idea of nullish values instead of falsey values

// Nullish Values : Null & Undefined (NOT 0 or '')

Logical Assignment Operators

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza : function (mainIngredient, ...otherIngredients)
  {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

/* ------------------------------------------------------ */
/*              Logical Assignment Operators              */
/* ------------------------------------------------------ */

const rest1 = {
  name: 'Capri',
  numGuests: 20,
};

const rest2 = {
  name: 'La Piazza',
  owner: 'Giovanni Rossi',
};

/* --------------- OR assignment operator --------------- */
// rest1.numGuests = rest1.numGuests || 10;
// rest2.numGuests = rest2.numGuests || 10;
// this is the same as writing :
rest1.numGuests ||= 10;
rest2.numGuests ||= 10;

console.log(rest1);
console.log(rest2);

// Similarly we can use ?? and && operators

Looping Arrays : The for-of Loop

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza : function (mainIngredient, ...otherIngredients)
  {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

/* ------------------------------------------------------ */
/*                       for-of loop                      */
/* ------------------------------------------------------ */

const menu = [...restaurant.starterMenu, ...restaurant.mainMenu];
 
for (const item of menu) console.log(item); // Prints: Focaccia \n Bruschetta ...

// to get the index and the array element both we use :
for (const item of menu.entries()) console.log(item); // Prints: [ 0, 'Focaccia' ] \n [ 1, 'Bruschetta' ] ...

for (const item of menu.entries())
{
  console.log(`${item[0]+1}: ${item[1]}`); // Prints: 1: Focaccia \n ...
}
// we did +1 above because in a real restaurant the list would start from a 1 and not a zero.


// we can also do it by destructuring the array : 
for (const [i, el] of menu.entries())
{
  console.log(`${i+1}: ${el}`); // Prints: 1: Focaccia \n ...
}

console.log(menu.entries()); // Prints: Object [Array Iterator] {}
console.log(...menu.entries()); // Prints: [ 0, 'Focaccia' ] [ 1, 'Bruschetta' ] [ 2, 'Garlic Bread' ] ...

Enhanced Object Literals :

First Enhancement : Writing Objects

'use strict';

const openingHours = {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  };

const restaurant = {
  name: 'Classico Italiano',

// suppose we want the openingHours object inside of restaurant object as well, then, we'll have to do it like this : 

  openingHours: openingHours, // the problem here is that the property name on the LHS is the same the variable name on RHS from which we are getting this new object. 
  // This is where ES6's Enhanced Object Literals comes in handy : 
  
  openingHours, // now we have a property called hours

};

Second Enhancement : Writing Methods :

In ES6 we no longer have to create a property, and then set it to a function expression like we have always been doing.

'use strict';

const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],


  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  // the above method can now be written as  ;
  order (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

};

Third Enhancement :

Instead of just writing the property names, we can now even compute the property names :

const weekDays = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
const openingHours = {
  [weekDays[3]]: {
    open: 12,
    close: 22,
  },
  [weekDays[4]]: {
    open: 11,
    close: 23,
  },
  [weekDays[5]]: {
    open: 0, // Open 24 hours
    close: 24,
  },
};

Optional Chaining :

The Optional Chaining operator  ?.  is used to check if variable immediately preceding the operator exists or not. By 'exists' we mean the nullish concept and not the truthy/false concept. A value is nullish only if it is either null or undefined. If the variable preceding the operator is nullish, then the execution of the statement will immediately return with the value of undefined. Else, the execution will proceed as normal.

'use strict';

// Data needed for a later exercise
const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';
/* ------------------------------------------------------ */
// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza : function (mainIngredient, ...otherIngredients)
  {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

// this is the trashy method of logging something if a thing exists, now imagine if we had to check whether multiple things exist or not then we'd have to use && operator which would make the code lengthy.
if (restaurant.openingHours && restaurant.openingHours.mon)
  console.log(restaurant.openingHours.mon.open);

if (restaurant.openingHours.fri)
  console.log(restaurant.openingHours.fri.open);


// ES2020 has a solution to this :  the ? operator i.e. Optional Chaining: 

//here the open propertry will be read only if restaurant.openingnHours.mon exists 
console.log(restaurant.openingHours.mon?.open);
// similarly we can do for openingHours
console.log(restaurant.openingHours?.mon?.open);

// Example
const days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

for (const day of days) {
  const open = restaurant.openingHours[day]?.open;
  console.log(`On ${day}, we open at ${open}`);
}
/* Prints: 
On mon, we open at undefined
On tue, we open at undefined
On wed, we open at undefined
On thu, we open at 12
On fri, we open at 11
On sat, we open at 0
On sun, we open at undefined */

// Now suppose we dont want the undefined value here then we'll do it like this : 
for (const day of days) {
  const open = restaurant.openingHours[day]?.open || 'closed';
  console.log(`On ${day}, we open at ${open}`);
}

/* Prints: 
On mon, we open at closed
On tue, we open at closed
On wed, we open at closed
On thu, we open at 12
On fri, we open at 11
On sat, we open at closed
On sun, we open at closed */

// now the problem is that on saturday the restaurant opens at 0 but we are getting closed here. To fix this we'll use the ?? operator here instead of || (DIY)

/* ------------ methods and Optional Chaining ----------- */
console.log(restaurant.order?.(0,1) ?? 'Method does not exist') 
// Prints: [ 'Focaccia', 'Pasta' ]

console.log(restaurant.orderCake?.(0,1) ?? 'Method does not exist') 
// Prints: Method does not exist

// Arrays
const users = [
  {name: 'Jonas',
  email: 'jonas@io.io'
  }
]
// Tip: when reading code basically replace '?' with 'if it exists' for convenience
console.log(users[0]?.name ?? 'Users array is empty'); // Prints: Jonas

Looping Objects: Object Keys, Values, Entries

'use strict';

// Data needed for a later exercise

const openingHours = {
  thu: {
    open: 12,
    close: 22,
  },
  fri: {
    open: 11,
    close: 23,
  },
  sat: {
    open: 0, // Open 24 hours
    close: 24,
  },
};

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';
/* ------------------------------------------------------ */
// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours,

  order (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza : function (mainIngredient, ...otherIngredients)
  {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

// looping over property names / keys in an object

// - Indirect Way : here we are not actually looping over the object itself but an array instead

/* ------------------- Property NAMES ------------------- */

const properties = Object.keys(openingHours);
console.log(properties); // Prints: [ 'thu', 'fri', 'sat' ] // which is an array

let openStr = `We are open on ${properties.length} days: `;

for (const day of Object.keys(openingHours))
{
  console.log(day);
/* Prints: 
   thu
   fri
   sat */
}

for (const day of Object.keys(openingHours))
{
  openStr += `${day}, `;
}
console.log(openStr); // Prints: We are open on 3 days: thu, fri, sat, 

/* ------------------- Property Values ------------------ */
const values = Object.values(openingHours);
console.log(values);
// Prints: 
/* [
  { open: 12, close: 22 },
  { open: 11, close: 23 },
  { open: 0, close: 24 }
] */

/* -------------------- Entire object ------------------- */
const entries = Object.entries(openingHours);
console.log(entries);
// Prints: 
/* [
  [ 'thu', { open: 12, close: 22 } ],
  [ 'fri', { open: 11, close: 23 } ],
  [ 'sat', { open: 0, close: 24 } ]
] */

for (const [key, {open, close}] of entries) {
  console.log(`${key}: ${open}`);
}
// Prints: 
/* thu: 12
fri: 11
sat: 0 */

Sets and Maps

  • In ES6, two new data structures were added to JS - Sets and Maps.
  • A set is a collection of unique values - there can be no duplicate elements in a set. Hence, sets are often used to remove duplicate elements from an array.
  • A set can hold mixed datatypes.
  • In the args for the constructor of the set, we can pass in any kind of iterable. Since an array is an iterable, it works. Similarly, if you pass in a single string, that would work as well. The resulting set that would be created would contain the unique characters that are present in the string.

Set :

'use strict';

// Data needed for a later exercise

const openingHours = {
  thu: {
    open: 12,
    close: 22,
  },
  fri: {
    open: 11,
    close: 23,
  },
  sat: {
    open: 0, // Open 24 hours
    close: 24,
  },
};

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';
/* ------------------------------------------------------ */
// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours,

  order(starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza: function (mainIngredient, ...otherIngredients) {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

// to create a new set we write 'new Set()' inside of which we'll put an iterable such as an array :
const ordersSet = new Set(['Pasta', 'Pizza', 'Pizza', 'Risotto', 'Pasta']);
console.log(ordersSet); // Prints: Set(3) { 'Pasta', 'Pizza', 'Risotto' }
// we can see that all the duplicate values are gone
// set is also iterable
// order of elements in a set is irrelevant

// remember that string is also an iterable
console.log(new Set('Jonas')); // Prints: Set(5) { 'J', 'o', 'n', 'a', 's' }

console.log(ordersSet.size); // Prints: 3
console.log(ordersSet.has('Pizza')); // Prints: true
console.log(ordersSet.has('Bread')); // Prints: false

ordersSet.add('Garlic Bread');
ordersSet.add('Garlic Bread');
// here the 'Garlic Bread' will only be added once

ordersSet.delete('Risotto');
console.log(ordersSet); // Prints: Set(3) { 'Pasta', 'Pizza', 'Garlic Bread' }

ordersSet.clear();
/* ------------------------------------------------------ */
// how do we retrieve values out of the set ?
console.log(ordersSet[0]); // Prints: error // this doesn't work hereṇ 
// there is actually no way of getting values out of a set.
// it makes sense as a set does not store duplicate values, all we need to know whether a value is in the set or not.

// if your goal is to store values in order and then retrieve it then the best use case, is to just use an array.
/* ------------------------------------------------------ */

// if we want to see what all different positions are available in a restaurant then we'll do this : 
const staff = ['Waiter', 'Chef', 'Waiter', 'Manager', 'Chef', 'Waiter'];
const staffUnique = new Set(staff) // Prints: Set(3) { 'Waiter', 'Chef', 'Manager' }

console.log(staffUnique);

const staffUnique2 = [...new Set(staff)]
console.log(staffUnique2); // Prints: [ 'Waiter', 'Chef', 'Manager' ]

// this can be used to check how many unique values are there
console.log(new Set(['waiter', 'waiter', 'manager']).size); // Prints: 2
console.log(new Set('hitarth').size); // Prints: 5 // instead of 7

Maps :

  • Just like an object, the data in a map is stored as key-value pairs. The difference between the two is that in the case of maps, the keys can have any type, whereas in the case of objects, the keys have to be strings. So maps can have an object, an array, or even another map as a key.
  • Note that the insertion order is maintained in maps in JS as mentioned here on SO.
const rest = new Map();
rest.set('name', 'Classico Italiano');
rest.set(1, 'Firenze, Italy');
console.log(rest.set(2, 'Libson, Portugal'));
  • Output :
    Pasted image 20231219164555.png
'use strict';

// Data needed for a later exercise

const openingHours = {
  thu: {
    open: 12,
    close: 22,
  },
  fri: {
    open: 11,
    close: 23,
  },
  sat: {
    open: 0, // Open 24 hours
    close: 24,
  },
};

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';
/* ------------------------------------------------------ */
// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours,

  order(starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza: function (mainIngredient, ...otherIngredients) {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

/* ------------------------------------------------------ */

const rest = new Map();
rest.set('name', 'Classico Italiano');
rest.set(1, 'Firenze, Italy');
console.log(rest.set(2, 'Libson, Portugal'));

rest
  .set('categories', ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'])
  .set('open', 11)
  .set('close', 23)
  .set(true, 'We are open :D')
  .set(false, 'We are closed')

// in order to read data from the map we use the 'get' method
console.log(rest.get('name')); // Prints: Classico Italiano
console.log(rest.get('1')); // Prints: undefined
console.log(rest.get(1)); // Prints: Firenze, Italy

const time = 21;
console.log(rest.get(time > rest.get('open') && time < rest.get('close')));
// the statement will be resolved to give console.log(rest.get(true)); which will then further be resolved to Print: We are open :D


//has
console.log(rest.has('categories')); // Prints: true
console.log(rest.has('We are open :D')); // Prints: false

//delete
rest.delete(2); // deletes (2, 'Libson, Portugal')
console.log(rest.size); // Prints: 7
// rest.clear();

// arrays and objects as map keys : 
rest.set([1,2], 'Test')
console.log(rest); // Prints: ... \n [ 1, 2 ] => 'Test'
console.log(rest.size);

console.log(rest.get([1,2])); // Prints: undefined
// here undefined is printed because the arrays in .set() and .get() are not the same arrays in heap.

// however if they are indeed the same array then it'll work
const arr = [3,4];
rest.set(arr, 'so desu ne');
console.log(rest.get(arr)); // Prints: so desu ne

Maps Iteration :

'use strict';

// Data needed for a later exercise

const openingHours = {
  thu: {
    open: 12,
    close: 22,
  },
  fri: {
    open: 11,
    close: 23,
  },
  sat: {
    open: 0, // Open 24 hours
    close: 24,
  },
};

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';
/* ------------------------------------------------------ */
// Data needed for first part of the section
const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours,

  order(starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPizza: function (mainIngredient, ...otherIngredients) {
    console.log(mainIngredient);
    console.log(otherIngredients);
  },
};

/* ------------------------------------------------------ */

const question = new Map
([
  ['question', 'What is the best language?'],
  [1, 'C'],
  [2, 'Java'],
  [3, 'JavaScript'],
  ['correct', 3],
  [true, 'correct answer !'],
  [false, 'wrong answer !'],
]);
// the above structure looks similar to the structure returned by Object.entries()

console.log(Object.entries(openingHours));
// Prints: 
/* [
  [ 'thu', { open: 12, close: 22 } ],
  [ 'fri', { open: 11, close: 23 } ],
  [ 'sat', { open: 0, close: 24 } ]
] */

// so, objects can be easily converted to a map :
/* ------------ converting an object to a map ----------- */
const hoursMap = new Map (Object.entries(openingHours));
console.log(hoursMap);
// Prints: 
/* Map(3) {
  'thu' => { open: 12, close: 22 },
  'fri' => { open: 11, close: 23 },
  'sat' => { open: 0, close: 24 }
} */
/* ------------------------------------------------------ */

/* ---------------------- iteration --------------------- */
//Quiz App
console.log(question.get('question'));
for (const [key, value] of question)
{
  if(typeof key === 'number')
    console.log(`${key}: ${value}`);
}
// const answer = Number(prompt('Your answer'))
const answer =3;
console.log((question.get(answer === question.get('correct'))));
/* ------------------------------------------------------ */

/* ---------------- convert map to array ---------------- */
console.log(...question);
// Prints: 
/* [ 'question', 'What is the best language?' ] [ 1, 'C' ] [ 2, 'Java' ] [ 3, 'JavaScript' ] [ 'correct', 3 ] [ true, 'correct answer !' ] [ false, 'wrong answer !' ] */
/* ------------------------------------------------------ */
/* ------------------- other operators ------------------ */
console.log(question.entries());
console.log(question.keys());
console.log(question.values());


console.log(question.keys()); // Prints: [Map Iterator] { 'question', 1, 2, 3, 'correct', true, false }

console.log(...question.keys()); // Prints: question 1 2 3 correct true false

Which Data structure to use ?

Pasted image 20231219201113.png

Pasted image 20231219201901.png

Strings :

indexOf() & slice() :

const airline = 'TAP Air Portugal';
const plane = 'A320';

/* ----------------------- length ----------------------- */
console.log(plane[2]); // Prints: 1
console.log(airline.length); // Prints: 16
console.log('B737'.length); // Prints: 4

console.log(airline.indexOf('T')); // Prints: 0

// Search from the end of the string
console.log(airline.lastIndexOf('r')); // Prints: 10

// Search for the occurrence of the word
console.log(airline.indexOf('portugal')); // Prints: -1. Because it is case-sensitive and string contains 'Portugal' not 'portugal'

// Slice the string starting from index 4 to the end of the string
console.log(airline.slice(4)); // Prints: 'Air Portugal'

// Slice the string from index 4 included, index 7 excluded
console.log(airline.slice(4, 7)); // Prints: 'Air'

// Start from the index 0, right up to the first occurrence of the ' '
console.log(airline.slice(0, airline.indexOf(' '))); // Prints: 'TAP ' // Note that there is a space.

// Start from the index AFTER the last index of ' ', right up to the end of the string
console.log(airline.slice(airline.lastIndexOf(' ') + 1)); // Prints: 'Portugal'

// Start from the end of the string. Print the first two characters
console.log(airline.slice(-2)); // Prints: 'al'

// Start from the index 1 print up to the index before the last one
console.log(airline.slice(1, -1)); // Prints: 'AP Air Portuga'

// Whenever we call a method on a string JS behind the scenes will convert the string primitive to a string object and it's on that object where methods are called. This process is called Boxing.
// JS internally changes the type of string by boxing and unboxing as required
console.log(typeof 'JavaScript Strings'); // Prints: string
console.log(typeof new String('JavaScript Strings')); // Prints: object. Warning: Primitive type object wrapper used
console.log(typeof String('JavaScript Strings')); // Prints: string

toLowerCase, toUpperCase, includes, replace, replaceAll, includes, startsWith, endsWith :

"use strict";


const airline = "TAP Air Portugal";
console.log(airline.toLowerCase()); // → tap air portugal
console.log(airline.toUpperCase()); // → TAP AIR PORTUGAL

// Fix capitalization in name
const passenger = "jOnAS";
const passengerLower = passenger.toLowerCase();
const passengerCorrect =
  passengerLower[0].toUpperCase() + passengerLower.slice(1);
console.log(passengerCorrect); // → Jonas

// Check email
const email = "hello@jonas.io";
const loginEmail = " Hello@Jonas.IO \n";
const normalizedEmail = loginEmail.toLowerCase().trim();
console.log(normalizedEmail === email); // → true

// Replacing : .replace only replaces the first appearance of the character/word
const priceGB = "288,98£";
const priceUS = priceGB.replace("£", "$").replace(",", ".");
console.log(priceUS); // → 288.98$

const announcement =
  "All passengers come to boarding door 23. Boarding door 23!";
console.log(announcement.replaceAll("door", "gate"));
// → All passengers come to boarding gate 23. Boarding gate 23!

// Booleans
const plane = "Airbus A320neo";
console.log(plane.includes("A320")); // → true
console.log(plane.startsWith("Air")); // → true

if (plane.startsWith("Airbus") && plane.endsWith("neo")) {
  console.log("Part of the NEW Airbus family");
} // → Part of the NEW Airbus family

// Practice exercise
const checkBaggage = function (items) {
  const baggage = items.toLowerCase();

  if (baggage.includes("knife") || baggage.includes("gun")) {
    console.log("You are NOT allowed on board!");
  } else {
    console.log("Welcome abroad!");
  }
};

checkBaggage("I have a laptop, some Food and a pocket knife");
// → You are NOT allowed on board!
checkBaggage("Socks and camera");
// → Welcome abroad!
checkBaggage("Got some snacks and a gun for protection");
// → You are NOT allowed on board!

More String Functions :

"use strict";

console.log("a+very+nice+string".split("+"));
// → (4) ['a', 'very', 'nice', 'string']

// Split and Join
const [firstName, lastName] = "Pavel Georgiev".split(" ");
const newName = ["Mr.", firstName, lastName.toUpperCase()].join(" ");
console.log(newName); // → Mr. Pavel GEORGIEV

const capitalizeName = function (name) {
  const names = name.split(" ");
  const namesUpper = [];

  for (const n of names) {
    namesUpper.push(n.replace(n[0], n[0].toUpperCase()));
  }

  console.log(namesUpper.join(" "));
};

capitalizeName("jessica ann smith davis"); // → Jessica Ann Smith Davis
capitalizeName("pavel georgiev"); // → Pavel Georgiev

// Padding
const message = "Go to gate 23!";
console.log(message.padStart(25, "+")); // → +++++++++++Go to gate 23!
console.log(message.padEnd(25, "+")); // → Go to gate 23!+++++++++++

const maskCreditCard = function (number) {
  const str = number + ""; // to convert the number to a string
  const last = str.slice(-4);
  return last.padStart(str.length, "*");
};

console.log(maskCreditCard(32415216)); // → ****5216
console.log(maskCreditCard("62416278")); // → ****6278

// Repeat
const message2 = "foo ";
console.log(message2.repeat(5)); // → foo foo foo foo foo

const planesInLine = function (n) {
  console.log(`There are ${n} planes in line ${"🛩".repeat(n)}`);
};
planesInLine(5); // → There are 5 planes in line 🛩🛩🛩🛩🛩
planesInLine(3); // → There are 3 planes in line 🛩🛩🛩
planesInLine(12); // → There are 12 planes in line 🛩🛩🛩🛩🛩🛩🛩🛩🛩🛩🛩🛩

API text formatting (simple) :

'use strict';

const flights =
  '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30';

  /* ------------------------------------------------------ */
// 🔴 Delayed Departure from FAO to TXL (11h25)
//              Arrival from BRU to FAO (11h45)
//   🔴 Delayed Arrival from HEL to FAO (12h05)
//            Departure from FAO to LIS (12h30)
/* ------------------------------------------------------ */

for ( const flight of flights.split('+'))
{
    const [type, from, to, time] = flight.split(';');
    const output = `${type.startsWith('_Delayed') ? '🔴' : ''}${type.replaceAll('_', ' ')} from ${from.slice(0, 3).toUpperCase()} to ${to.slice(0, 3).toUpperCase()} (${time.replace(':', 'h')})`.padStart(44);
    console.log(output);
}