Destructuring Assignment

The two most used data structures in JavaScript are Object and Array.

  • Objects allow us to create a single entity that stores data items by key.
  • Arrays allow us to gather data items into an ordered list.

Destructuring Assignment is a JavaScript expression that allows to unpack values from arrays, or properties from objects, into distinct variables data can be extracted from arrays, objects, nested objects, and assigning to variables.

My Definition: What actually do we want from Objects and Arrays? Just store them with variables and call them when needed.

Destructuring - Arrays, and Objects by Anisul Islam

    //==============================
    // Destructuring by Anisul Islam
    //==============================

    //==
    // array destructuring //
    //==
    let numbers = [10, 20, 30, 40, 50];

    // normal method
    console.log(numbers[0]);
    console.log(numbers[1]);

    // destructing method =>
    let [num1, num2, num3, num4, num5] = numbers;
    // let [num1, num2, num3, ...z] = numbers;
    console.log(num1);
    console.log(num2);
    console.log(num1, num2, num3);

          // spread operator
          // let [num01, num02, ...z] = numbers;
          // console.log(z) // returns: [40, 50]
    //==
    // swap  variables
    //==

    let a = 10, b = 20;
    [a, b] = [b, a];
    console.log(a); //returns: 20
    console.log(b); // returns: 10

    //==
    // object destructuring //
    //==

    const studentInfo = {
      id: 101,
      fullName : 'Nur Rafi',
      gpa: 3.75,
      language: {
        native: 'Bangla',
        beginner: 'English',
      }
    }
    // normal method
    console.log(studentInfo.gpa);

    // destructing method =>
    const {id, fullName, language} = studentInfo;
    console.log(fullName, language.native);

    //==
    // nested object destructuring //
    //==
	
    console.log(language.native)

    //==
    // destructuring function parameters //
    //==

    const student = {
      id: 777,
      fullName: 'Nur Rafi',
      subject: 'JavaScript'
    }
	
    // normal method
    const studentData = (student) => {
    return console.log(`${student.id}, ${student.fullName}, ${student.subject}`)
    };
    studentData(student);

    // destructing method =>
    const studentDataDst = ({id, fullName, subject}) => {
      return console.log(`${id}, ${fullName}, ${subject}`)
      };
    studentDataDst(student);


Destructuring Arrays

    // =================== //
    // Destructuring Array //
    // =================== //

    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]];
      },
    };

    const arr = [2, 3, 4];
    // normal method
    const a = arr[0];
    const b = arr[1];
    const c = arr[2];

    // destructuring method =>
    const [x, y, z] = arr; // This is not array, just a Distructuring assignment
    console.log(x, y);
    console.log(x, y, z);


    /// Restaurant Categories ///===>

    // first and second
    const [first, second] = restaurant.categories;
    console.log(first, second);

    // first and third
    let [main, , secondary] = restaurant.mainMenu; // skipping elements using skip(,)
    console.log(main, secondary); // returns: Italian Vegetarian


    /// Reverse or Swap or Switching Variables ///===>

    // without distructuring =>
    const temp = main; // temporary variable
    main = secondary;
    secondary = temp;
    console.log(main, secondary); // returns: Vegetarian Italian

    // distructuring method =>
    [main, secondary] = [secondary, main]
    console.log(main, secondary); // returns: Vegetarian Italian


    /// Distructuring Function ///===>

    // normal method
    const order = restaurant.order(2, 0);
    console.log(order) // returns: 'Garlic Bread', 'Pizza'

    // destructuring method =>
    // Receive 2 return values from a function
    const [starterIdx, mainIdx] = restaurant.order(2, 0); // Destruction Assignment = Calling a function with(parameters)
    console.log(starterIdx, mainIdx); // Returns: 'Garlic Bread', 'Pizza'


    /// Nested Destructuring ///===>

    const nested = [2, 4[5, 6]]; // nested = array inside an array
    const [i, , j] = nested;
    console.log(i, j); // returns 2 [5, 6]

    const [i, , [j, k]] = nested;
    console.log(i, j, k); // returns: 2, 5, 6


    /// Setup Default Values ///===>

    // normal method = undefined
    const [p, q, r] = [8, 9];
    console.log(p, q, r); // returns 8, 9, undefined

    // Default Value
    const [p = 1, q = 1, r = 1] = [8, 9];
    console.log(p, q, r); // returns 8, 9, 1
    // This is usefull when we get data from API


Destructuring Objects

    // ===================== //
    // Destructuring Objects //
    // ===================== //

    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,
        },
      },

      orderDelivery: function({starterIndex, mainIndex, time, address}){
        console.log(`Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be deliverd to ${address} at ${time}`);
      },
    };

    /// Object Calling function 🙁 ///===>
    restaurantName.orderDelivery({
      time: '22:30',
      address: 'Via del Sole, 21',
      mainIndex: 2,
      starterIndex: 2,
    }); // If there is lots of parameters, 1st write object property then write function parameters.


    /// Fundamental of Destructuring Objects ///===>
    const {name, openingHours, categories} = restaurant;
    console.log(name, openingHours, categories);
    // API data comes with Objects, So Destructuring is life saving method of Javascript. Like Weather Data, Movie Data //


    /// Setup New Variables ///===>

    const {name: restaurantName, openingHours: hours, categories: tags} = restaurant; 
    // What if we want a new variable name from the property name, So write old name as reference and assign new name.
    console.log(restaurantName, hours, tags) // Helpful for dealing with 3rd party data


    /// Setup default value which is [empty] ///===>
    const {
      menu = [], starterMenu: starters = []
    } = restaurantName;
    console.log(menu, starters); // menu does not exist on restaurent Object so value is [empty]
    // If we do not setup default value result will be [undefined]
    // In real life API data do not comes with hardcoded like restaurent Object
    // We do not know how will be data looks like, So setup default value is useful.


    /// Mutating Variables ///===> (Reassign Values to Variables)
 
    let a = 111;
    let b = 222;
    const obj = {a: 23, b: 7, c: 14};
    {a, b} = obj;
    console.log(a, b); // returns: Unexpected token error '='
    // solution is using parenthesis()
    ({a, b} = obj);
    console.log(a, b); // returns: 23, 7


    /// Nested Objects ///===>

    const {fri} = openingHours;
    console.log(fri); // {open: 11, close: 23}
    // but we need open and close separately
    const {fri:{open, close}} = openingHours; // we can go more deep like {{{}}}
    console.log(open, close);


Spread Operator(...)

    // Normal method ==>
    let numbers = [10, 20, 30, 40, 50];
    
    function calculate(a, b, c, d, e) {
      return a + b + c + d + e
    }
    console.log(calculate(numbers)); // returns: 10,20,30,40,50undefinedundefinedundefinedundefined


    // Destructuring Method ==>
    let numbers = [10, 20, 30, 40, 50];
    let [num1, num2, num3, num4, num5] = numbers;

    const calculate = function(){
      return num1 + num2 + num3 + num4 + num5
    }
    console.log(calculate(numbers)) // returns: 150

    // Spread Operator ==>
    let numbers = [10, 20, 30, 40, 50];
    
    function calculate(a, b, c, d, e) {
      return a + b + c + d + e
    }
    console.log(calculate(...numbers)); // returns: 150
    
  	// Calculate Unlimited number of arguments using Arguments Object
  	const numbers = [10, 20, 30, 40, 50]

  	function add() {
    	let result = 0;
    	for (let i = 0; i < arguments.length; i++) {
      	result += arguments[i]; // Same As: x = x + y
    	};
    	return result;
  	};
  	console.log(add(...numbers)); // SPREAD operator // returns: 150
    // ========================= //
    // The Spread Operator (...) //
    // ========================= //

      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,
        },
      },

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

    };


    /// BEST use for - Pass arguments into a function or build a new Array
    const arr = [7, 8, 9]; // Unpacking all element at one
    const badNewArr = [1, 2, arr[0], arr[1], arr[2]];
    console.log(badNewArr); // returns: [1, 2, 7, 8, 9]


    /// Using Spread Operator(...) ///===>

    const newArr = [1,2, ...arr]; // Automatically add comma(,) if there is other element with comma(,)
    console.log(newArr); // returns: [1, 2, 7, 8, 9]

    console.log(...newArr); // returns: 1 2 7 8 9 // Individual elements in the array

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

    // Spread Operator is bit similer to destructuring because it also helps us get an element out of array.
    // Big difference is spread operator take all the element from the array and it also does not create variables.


    /// Shallow Copy array ///===>

    const mainMenuCopy = [...restaurant.mainMenu]


    /// Join Multiple Arrays ///===>

    const menu = [...restaurant.starterMenu, ...restaurant.mainMenu]
    console.log(menu); // returns: ["Focaccia", "Bruschetta", "Garlic Bread", "Caprese Salad", "Pizza", "Pasta", "Risotto"]

    // Spread Operator work not only on Arrays basically on all irritable
    // What is irritable?
    // ==> Iterable: arrays, strings, maps, sets but NOT Objects and Numbers

    const str = 'Nur';
    const latters = [...str];
    console.log(...str); // returns: N u r
    console.log(latters); // returns: ['N', 'u', 'r']
    console.log(`${...str} Rafi`); // This is not a place that expects multiple value seperated by comma,
    const numbers = 35468431;
    const numbersStr = [...numbers];
    console.log(numbersStr); // returns: TypeError: numbers is not iterable


    const ingredients = [prompt("Let's make pasta! Ingredient 1?"), prompt("Ingredient 2?"), prompt("Ingredient 3?")];
    console.log(ingredients);

    console.log(restaurant.orderPasta(...ingredients));

    // Since 2018 Spread Operator(...) also works on Objects even though Objects are not Iterable.


    /// Objects ///===>

    const newRestaurant = {foundedIn: 2021, ...restaurant, founder: 'This is me'}
    console.log(newRestaurant);


    /// Shallow Copy Object ///===>

    const restaurantCopy = {...restaurant}
    console.log(restaurantCopy);
    restaurantCopy.name = 'Nur Rafi'
    console.log(restaurantCopy.name);
    console.log(restaurant.name);

    // We can build new Array and Object by copy them using Spread Operator(...) also edit them.


Rest Pattern and Parameters


Arguments Object



    /// Arguments Object ///===>

    function add(a, b) {
      return a + b;
    }

    console.log(2, 5);

      
    /// Calculate Unlimited number of arguments using Arguments Object ///===>

      const numbers = [10, 20, 30, 40, 50]

      function add() {
        let result = 0;
        for (let i = 0; i < arguments.length; i++) {
          result += arguments[i]; // Same As: x = x + y
        }
        return result;
      }
      console.log(add(...numbers)); // SPREAD operator

    // Arguments Object NOT available in Arrow function

    function add() {
      let result = 0;
      for (let i = 0; i < arguments.length; i++) {
        result += arguments[i]; // Addition Assignment Operator (+=)
      }
      return result;
    }
    console.log(add(10, 20)); // returns: ReferenceError: arguments is not defined

Rest Pattern looks exactly like Spread Operator so it has the same syntax with 3dots(...) but it does the opposite of Spread Operator.

    // =========================== //
    // Rest Pattern and Parameters //
    // =========================== //


    /// REST Parameter or Unused Parameter ///===>

    // SPREAD, because on RIGHT side of =
    const arr = [10, 20, ...[30, 40, 50]];

    // REST, becuase on LEFT side of =
    const [a, b, ...rest] = [10, 20, 30, 40, 50];

    console.log(a); // returns: 10
    console.log(b); // returns: 20
    console.log(rest); // returns: Array [30,40,50]

    /// Destructuring using REST parameter(...otherFood) and SPREAD operator(...)=> Add 2 array ///===>
    const [pizza, , risotto, ...otherFood] = [...restaurant.mainMenu, ...restaurant.starterMenu];
    console.log(pizza, risotto, otherFood); // returns: Pizza Risotto Arrays ["Focaccia", "Bruschetta", "Garlic Bread", "Caprese Salad"]


    /// Objects ///===>
    const {
      thu,
      fri,
      ...weekend
    } = restaurant.openingHours;
    console.log(weekend); // returns: sat: {open: 0, close: 24}

    // REST parameter in function
    function person(name, age, ...degree) {
      console.log(name); // returns: John
      console.log(age); // returns: 25
      console.log(degree); // returns: Array ['SSC', 'HSC']
    }
    person('John', 25, 'SSC', 'HSC');

    function add(...numbers) {
      console.log(numbers)
    };
    add(2, 3); // returns: [2, 3]
    add(2, 3, 4, 5); // returns: [2, 3, 4, 5]
    add(2, 3, 4, 5, 6, 7); // returns: [2, 3, 4, 5, 6, 7]

    // Calculate Unlimited number of arguments using Arguments Object
    const numbers = [10, 20, 30, 40, 50]

    function add() {
    let result = 0;
    for (let i = 0; i < arguments.length; i++) {
    result += arguments[i]; // Same As: x = x + y
    };
    return result;
    };
    console.log(add(...numbers)); // SPREAD operator // returns: 150


SPREAD Operator and REST Pattern syntax look exactly the same, but they work in opposite ways depending on where it is used.

Rest Pattern separated by commas(,) with variables and Spread Operators separated by commas(,) with values.

In Destructuring, Spread Operator is used on the right side of (=) & Rest Pattern is used on the left side of (=).

Rest Pattern is also known as Unused Parameter. Rest element must be the last element, there must be only one Rest element.

👉🏻 Spread Operator(...) => Spread Array | Unpack Values.

👉🏻 REST Parameter(...) => Compress Array | Pack Values.


Short Circuiting (&& and ||)

Falsy Value: 0, '', undefined, null, NaN(Not a Number)

👉🏻 OR(||) operator will return 1st Truthy value or simply last value if all of them are falsy value. Value does not need to be Boolean.

👉🏻 AND(&&) operator will return 1st Falsy Value or simply last value if all of them are true.


Practical Use

👉🏻 OR(||) operator for set Default Value & Avoid 
Falsy Value

👉🏻 AND(&&) operator to Execute 2nd Value if the 1st one is true.


Short-Circuiting

Use ANY data type, return ANY data type, short-circuiting also known as short-circuit evaluation.

If the 1st value is the truthy value then immediately return the 1st value. Javascript stop looking for other values.
  // ============================ //
  // Short Circuiting (&& and ||) //
  // ============================ //

  /// Falsy Value ///===>
  // 0, '', undefined, null, NaN(Not a Number)

  console.log(3 || 'Jonas'); // return: 3
  console.log('' || 'Jonas'); // return: Jonas
  console.log(true || 0); // returns: true
  console.log(undefined || null); // returns: null // the is no short-circuiting
  console.log(undefined || 0 || '' || 'Hello' || 313 || null) // returns: Hello

  /// Setting Default Value ///===>

  // Ternary Operator
  restaurant.numGuests = 23 // if 0 it will not work and value will be 10
  const guest1 = restaurant.numGuests ? restaurant.numGuests : 10;
  console.log(guest1);

  // OR(||) Operator and Short-Circuiting
  restaurant.numGuests = 23 // if 0 it will not work and value will be 10
  const guest2 = restaurant.numGuests || 10
  console.log(guest2) //

  // restaurant.numGuests = 0;
  // Both method will not work if the value is 0, but there is other SOLUTION

  console.log(0 && 'Jonas'); // returns: 0
  console.log(7 && 'Jonas'); // returns: Jonas
  console.log('Hello' && 313 || null && undefined) // returns: null


The Nullish Coalescing Operator (??)

  // ==================================== //
  // The Nullish Coalescing Operator (??) //
  // ==================================== //

  // ES 2020
  // Nullish: null and undefined (NOT 0 or '')
  restaurant.numGuests = 0;
  const guest = restaurant.numGuests ?? 10
  console.log(guest) // return: 0

  // 0 and '' is truthy value for Nullish Coalescing Operator


Optional Chaining Operator(.?)

The optional chaining operator (?.) allows you to access the value of a property located deep within a chain of objects, If a certain property does not exist then return undefined immediately.

Nullish Coalescing Operator(??) 🤝🏻 Optional Chaining Operator(.?)

  // ============================== //
  // Optional Chaining Operator(.?) //
  // ============================== //

  if (restaurant.openingHours && restaurant.openingHours.mon) console.log(restaurant.openingHours.mon.open) // returns: Error

  // WITH Optional Chaining
  console.log(restaurant.openingHours.mon?.open);

  // MULTIPLE Optional Chaining
  console.log(restaurant.openingHours?.mon?.open);


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

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

  for (const day of days) {
  const open = shop.openingHours[day]?.open; // Using a variable to refer to a property name, we need to use [day] Notation.
  console.log(`On ${day}, we open at ${open}`);
  
  // Set Default Value Instead of UNDEFINED using OR(||) Operator
  const open = shop.openingHours[day]?.open || 'closed';
  console.log(`On ${day}, we open at ${open}`);

  // If value is 0 or '' use AND(??) Operator
  const open = shop.openingHours[day]?.open ?? 'closed';
  console.log(`On ${day}, we open at ${open}`);
  };


Enhanced Object Literals


Object literals are defined using the following syntax rules:

👉🏻 A colon(:) separates property name from the value.

👉🏻 A comma(,) separates each name-value pair from the next.

👉🏻 There should be no comma after the last name-value pair.

If any of the syntax rules are broken, such as a missing comma(,) or colon(:) or curly bracket{}, an error will be thrown.

Enhanced Object literals

1. Write Variable Name instead of Property Name.

2. Write Function Expression instead of Function Declaration with Property Name.

3. Compute Property Name instead of writing manually.

    // ======================== //
    // Enhanced Object Literals //
    // ======================== //
    
    // 1. Write Variable Name instead of Property Name.

    // 2. Write Function Expression instead of Function Declaration with Property Name.

    // 3. Compute Property Name instead of writing manually.

    const weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
    const openingHours = {
        weekdays[3]: {
          open: 12,
          close: 22,
        },
        weekdays[4]: {
          open: 11,
          close: 23,
        },
        [`day-${2 + 4}`]: {
          open: 0, // Open 24 hours
          close: 24,
        },
      };

    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'],

      // Before ES6
      openingHours: openingHours,

      // ES6 Enhanced Object Literal
      openingHours,

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


Looping Arrays: The for-of Loop

    // =============================== //
    // Looping Arrays: The for-of Loop //
    // =============================== //

    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,
            },
        },
    };

    const menu = [...restaurant.starterMenu, ...restaurant.mainMenu];

    /// 1. Normal Method ///
    for (const item of menu) console.log(item); 
    // returns: each element line by line ==> 
    
    Focaccia
    Bruschetta
    Garlic Bread
    Caprese Salad
    Pizza
    Pasta
    Risotto
    

    // NEED to write single line OR use {} brackets for output
    // We can use 'continue' and 'break' keywords
    // Index/Length will not work here, So we have to use entries()


    /// 2. .entries() => Array: [Index with Items] ///
    for (const item of menu.entries()) console.log(item); 
    // returns: each element line by line with index number but look like Array ==>
    
    [0, "Focaccia"]
    [1, "Bruschetta"]
    [2, "Garlic Bread"]
    [3, "Caprese Salad"]
    [4, "Pizza"]
    [5, "Pasta"]
    [6, "Risotto"]
    

    console.log(menu.entries()) // returns: Array Iterator {}
    console.log([...menu.entries()]);
    // returns: Array Within Arrays ==> 
    
    [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)]
    


    /// 3. Used by Normal Method ///
    for (const item of menu.entries()) console.log(`${item[0]+1}: ${item[1]}`)
    // returns: each item lile by line with index number ==>
    
    1: Focaccia
    2: Bruschetta
    3: Garlic Bread
    4: Caprese Salad
    5: Pizza
    6: Pasta
    7: Risotto
    

    
    /// 4. Used by Destructuring Method ///
    for (const [i, el] of menu.entries()) console.log(`${i+1}: ${el}`);
    // returns: each item lile by line with index number ==>
    
    1: Focaccia
    2: Bruschetta
    3: Garlic Bread
    4: Caprese Salad
    5: Pizza
    6: Pasta
    7: Risotto
    


Looping Objects: Object Keys, Values, and Entries

  • Object.keys(obj) – returns an array of keys.
  • Object.values(obj) – returns an array of values.
  • Object.entries(obj) – returns an array of [key, value] pairs.
    // ================================================= //
    // Looping Objects: Object Keys, Values, and Entries //
    // ================================================= //

    const openingHours = restaurant.openingHours // Object

    /// Object Keys ///==>

    // WITHOUT Loop <===> Array
    console.log(Object.keys(openingHours));
    // returns: Array: ["thu", "fri", "sat"]

    // WITH Loop <===> Line by Line
    for(const day of Object.keys(openingHours)) console.log(day);
    
    // returns: List of available Key/Property name of object ==>
    fri
    sat
    thu
    

    console.log(Object.keys(openingHours).length) // returns: Number of properties

    const properties = Object.keys(openingHours);
    let openStr = `We are open on ${properties.length} days:`;
    for(const day of properties){
        openStr += ` ${day},` // Sentence + Property with dynamic loop value
    };
    console.log(openStr)

    /// Object Values ///==>

    // WITHOUT Loop <===> Array
    console.log(Object.values(openingHours));

    // WITH Loop <===> Line by Line
    for(const value of Object.values(openingHours)) console.log(value);
    
    // returns:
    {open: 12, close: 22}
    {open: 11, close: 23}
    {open: 0, close: 24}
    

    /// Entrie Object ///==>

    const entries = Object.entries(openingHours)
    console.log(entries) // returns: Index [key, value]

    for(const [key, {open, close}] of entries){
        console.log(`On ${key} we open at ${open} and close at ${close}`);
    };
    
    // returns:
    On thu we open at 12 and close at 22
    On fri we open at 11 and close at 23
    On sat we open at 0 and close at 24
    


Sets & Maps

Till now, we’ve learned about the following complex data structures:

  • Objects are used for storing keyed collections.
  • Arrays are used for storing ordered collections.

But that’s not enough for real life. That’s why Set and  Map also exist.


Set

A Set is a special type collection – “set of values” (without keys), where each value may occur only once. There is no Index and no need to get data out of a set. The main use case is to remove duplicate values of a set


Its main methods are:
  • new Set(iterable) – creates the set, and if an iterable object is provided (usually an array), copies values from it into the set.
  • set.add(value) – adds a value, returns the set itself.
  • set.delete(value) – removes the value, returns true if value existed at the moment of the call, otherwise false.
  • set.has(value) – returns true if the value exists in the set, otherwise false.
  • set.clear() – removes everything from the set.
  • set.size – is the elements count.

The main feature is that repeated calls of set.add(value) with the same value don’t do anything. That’s the reason why each value appears in a Set only once.


    // === //
    // Set //
    // === //

    const ordersSet = new Set(['Pasta', 'Pizza', 'Pizza', 'Pasta', 'Sandwich'])
    console.log(ordersSet) // returns: Set(3) {"Pasta", "Pizza", "Sandwich"}

    console.log(new Set('Rafe')); // returns: Set(4) {"R", "a", "f", "e"}

    console.log(ordersSet.size); // returns: 3
    console.log(ordersSet.has('Pizza')); // returns: true
    console.log(ordersSet.has('Burger')); // returns: false
    ordersSet.add('Garlic Bread');
    ordersSet.delete('Pizza')
    console.log(ordersSet) // returns: Set(4) {"Pasta", "Sandwich", "Garlic Bread"}
    ordersSet.clear() // returns: clear set

    for (const order of ordersSet) console.log(order)

    // Example
    const staff = ['Waiter', 'Chef', 'Waiter', 'Manager', 'Chef', 'Waiter'];
    // Q: How many different positions are in a restaurent?
    const staffUnique = new Set(staff);
    console.log(staffUnique.size); // returns: 3

    // Convert Set to Array using Spread Operator [...]
    const staffArray = [...new Set(staff)];
    console.log(staffArray) // returns: ["Waiter", "Chef", "Manager"]

    // How many characters in my name
    console.log(new Set('nurmohamodrafi').size) // returns: 10


Map

The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.

Map has 3 default parameters (key, value, map)

Methods and properties are:

  • new Map() – creates the map.
  • map.set(key, value) – stores the value by the key.
  • map.get(key) – returns the value by the key, undefined if key doesn’t exist in map.
  • map.has(key) – returns true if the key exists, false otherwise.
  • map.delete(key) – removes the value by the key.
  • map.clear() – removes everything from the map.
  • map.size – returns the current element count.

    // === //
    // Map //
    // === //

    const numbers = [10, 20, 30, 40];
    const names = ['Tamim', 'Sakib', 'Musfiq'];
    const object = [{
            name: 'Humayun Ahmed',
            availableBooks: 30
        },
        {
            name: 'Jafar Iqbal',
            availableBooks: 20
        },
        {
            name: 'Ahmed Sofa',
            availableBooks: 10
        },
    ];

    // By Default Map has 3 parameters | ForEach Loop also has 3 parameters
    const newNumber = numbers.map(function (currentValue, index, arr) {
        console.log(currentValue)
    })

    // Callback function
    const newName = names.map(function (name) {
        return name.toUpperCase()
    })
    console.log(newName); // returns: ["TAMIM", "SAKIB", "MUSFIQ"]

    // Arrow function
    const newNameArr = names.map(names => {
        return names.toUpperCase()
    });
    console.log(newNameArr); // returns: ["TAMIM", "SAKIB", "MUSFIQ"]

    const newObject = object.map(item => {
        return item.name;
    });
    console.log(newObject) // return: ["Humayun Ahmed", "Jafar Iqbal", "Ahmed Sofa"]

	// But Keep adding [key, values] programmatically SET method is BEST
    
    const rest = new Map();
    rest.set('name', 'Hotel Redison');
    rest.set('location', 'Dhaka, Bangladesh');
    rest.set('categories', ['Deshi', 'Indian', 'Chinese', 'Italiano']).set('open', 11).set('close', 23).set(true, 'We are open :D').set(false, 'We are closed :(');

    console.log(rest.get('name')); // returns: Hotel Redison
    console.log(rest.get('location')); // returns: Dhaka, Bangladesh

    console.log(rest.get(true)); // returns: We are open :D
    const time = 21;
    console.log(rest.get(time > rest.get('open') && time < rest.get('close'))); // strict mode, all true = true OR 1 false all of them = false

    // truthy/falsy value AND conditional true/false, BOTH are different

    console.log(rest.has('categories')) // returns: true


Map Iteration

    // ============= //
    // Map Iteration //
    // ============= //

    // Scratch to Code this method is BEST

    const question = new Map([
        ['question', 'What is the best programming language in the world?'],
        [1, 'C'],
        [2, 'Java'],
        [3, 'Javascript'],
        ['correct', 3],
        [true, 'Correct'],
        [false, 'Try again!']
    ]);
    

    // Convert Object to Map
    const hoursMap = new Map(Object.entries(openingHours));
    console.log(hoursMap)

    // Quiz app
    console.log(question.get('question')); // returns: question
    for (const [key, value] of question) {
        if (typeof key === 'number') console.log(`Answer ${key}: ${value}`); // returns: 1-3 options
    };
    const answer = Number(prompt('Your answer'));
    console.log(answer); // returns: Input Answer
    console.log(question.get(question.get('correct') === answer)); // Correct === Answer 
    // (===) USE Only for single Input/Output NOT for multiple output.
// Convert Map to Array console.log([...question]); // Arrays of Arrays // console.log(question.entries()); console.log([...question.keys()]); console.log([...question.values()]);


Which Data Structure to Use



Coding Challenge

Let's continue with our football betting app! This time, we have a map called 'gameEvents' (see below) with a log of the events that happened during the game. The values are the events themselves, and the keys are the minutes in which each event happened (a football game has 90 minutes plus some extra time).


Your tasks:


1. Create an array 'events' of the different game events that happened (no duplicates)

2. After the game has finished, it was found that the yellow card from minute 64 was unfair. So remove this event from the game events log.

3. Compute and log the following string to the console: "An event happened, on average, every 9 minutes" (keep in mind that a game has 90 minutes)

4. Loop over 'gameEvents' and log each element to the console, marking whether it's in the first half or second half (after 45 min) of the game, like this:

[FIRST HALF] 17: ⚽ GOAL

    const gameEvents = new Map([
        [17, '⚽ GOAL'],
        [36, '� Substitution'],
        [47, '⚽ GOAL'],
        [61, '� Substitution'],
        [64, '� Yellow card'],
        [69, '� Red card'],
        [70, '� Substitution'],
        [72, '� Substitution'],
        [76, '⚽ GOAL'],
        [80, '⚽ GOAL'],
        [92, '� Yellow card'],
    ]);

    // 1
    // const events = [...new Set([...gameEvents.values()])]
    // Convert Set to Array just use Spread Operator[...] 1 time
    const events = [...new Set(gameEvents.values())];
    console.log(events) // all events WITHOUT duplicate
    // returns: ["⚽ GOAL", "� Substitution", "� Yellow card", "� Red card"]

    // 2
    gameEvents.delete(64)
    console.log(gameEvents) // delete key 64

    // 3
    // We can write 92 minutes but we want that value dynamically
    const times = [...gameEvents.keys()].pop()
    // gameEvents.keys() shows us all keys but we need last value dynamically so .pop() delete the last element and give it to us
    console.log(times); // find out last value ==> 92
    console.log(`An event happened, on average, every ${times/gameEvents.size} minutes`);

    // 4

    for (const [min, event] of gameEvents) {
        const half = min <= 45 ? 'FIRST' : 'SECOND'
        console.log(`[${half} HALF] ${min}: ${event}`)

    }; // returns:
        [FIRST HALF] 17: ⚽ GOAL
        [FIRST HALF] 36: � Substitution
        [SECOND HALF] 47: ⚽ GOAL
        [SECOND HALF] 61: � Substitution
        [SECOND HALF] 69: � Red card
        [SECOND HALF] 70: � Substitution
        [SECOND HALF] 72: � Substitution
        [SECOND HALF] 76: ⚽ GOAL
        [SECOND HALF] 80: ⚽ GOAL
        [SECOND HALF] 92: � Yellow card


Working With Strings

    // ==================== //
    // Working With Strings //
    // ==================== //

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

    console.log(plane[0]); //  A
    console.log(plane[1]); //  3
    console.log(plane[2]); //  2
    console.log('B737' [0]); //  B

    console.log(airline.length); //  16
    console.log('B737'.length); //  4

    console.log(airline.indexOf('r')); //  position at 6
    console.log(airline.lastIndexOf('r')); // returns: position at 10
    console.log(airline.indexOf('portugal')); //  -1
    console.log(airline.indexOf('Portugal')); //  8

    console.log(airline.slice(4)); // Only Start //  Air Portugal
    console.log(airline.slice(4, 7)); // End-Beginning //  Air

    console.log(airline.slice(0, airline.indexOf(' '))) //  TAP
    console.log(airline.slice(airline.lastIndexOf(' ') + 1)) //  Portugal

    console.log(airline.slice(0, -1) + 1) //  TAP Air Portugal
    console.log(airline.slice(-1)) // last Character

    const checkMiddleSeat = function (seat) {
        // B and E are middle seats
        const s = seat.slice(-1);
        if (s === 'B' || s === 'E') console.log('You got the middle seat 😖')
        else console.log('You got lucky 😃')
    };
    checkMiddleSeat('11B'); // You got the middle seat 😖
    checkMiddleSeat('23C'); // You got lucky 😃
    checkMiddleSeat('3E'); // You got the middle seat 😖

    console.log(new String('Rafe')) //  String {"Rafe"}
    // Whenever we call a Method on a String, Javascript convert each character individually to an Object AND when the operation is done convert back to again String.

    console.log(typeof new String('Rafe')) // Object
    console.log(typeof new String('Rafe').slice(1)) // String

    console.log(airline.toLowerCase()); // tap air portugal
    console.log(airline.toUpperCase()); // TAP AIR PORTUGAL


    // Fix Capitalization in name
    const passenger = 'jOnAs' // Should look like this
    const passengerLower = passenger.toLocaleLowerCase(); // convert all to lower case
    const passengerCorrect = passengerLower[0].toUpperCase() + passengerLower.slice(1); // convert 1st one Upper and rest of lower case
    console.log(passengerCorrect)

    // Fix Capitalization using function

    const fixName = function (name) {
        const lowercase = name.toLowerCase();
        const correct = lowercase[0].toUpperCase() + lowercase.slice(1);
        return correct;
    }
    console.log(fixName('nUrMohAmmoD'))

    // Comparing emails
    const email = 'hello@rafe.io';
    const loginEmail = ' Hello@Rafe.Io \n';

    const lowerEmail = loginEmail.toLowerCase();
    const trimmedEmail = lowerEmail.trim();
    console.log(trimmedEmail); //hello@rafe.io

    // Single line code
    const normalizeEmail = loginEmail.toLowerCase().trim();
    console.log(normalizeEmail) // hello@rafe.io
    console.log(email === normalizeEmail);

    // Replacing
    const priceGB = '288,97£' // Replace comma(,) and pound sign(£)
    const priceUS = priceGB.replace(',', '.').replace('£', '$');
    console.log(priceUS); // 288.97$

    const announcement = 'All passengers come to boarding door 23. Boarding door 23!'
    console.log(announcement.replace('door', 'gate')); // only replace single word
    // Replace All()
    console.log(announcement.replaceAll('door', 'gate')); // Replace multiple words

    // Regular Expression
    console.log(announcement.replace(/door/g, 'gate')); // Replace multiple words

    // Booleans
    const airbus = 'Airbus A320neo';
    console.log(airbus.includes('A320')); // true
    console.log(airbus.includes('Boeing')); // false
    console.log(airbus.startsWith('Air')); // true
    console.log(airbus.endsWith('neo')); // true

    if (airbus.startsWith('Airbus') && airbus.endsWith('neo')) {
        console.log('Part of the NEW Airbus family');
    }

    // Practice
    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('You are Welcome!');
        }
    };
    checkBaggage('I have a laptop, some Food and a pocket Knife');
    checkBaggage('Socks and camera');
    checkBaggage('Got some snacks and gun for protection');


    // Split & Join
    console.log('A+very+nice+movie'.split('+')); // ["A", "very", "nice", "movie"]
    console.log('Nur Mohamod Rafi'.split(' ')); // ["Nur", "Mohamod", "Rafi"]

    // Split output Array so Loop is available

    const [firstName, lastName] = 'Nur Mohamod Rafi'.split(' ');

    console.log(firstName);
    console.log(lastName);

    // Join('') Method is OPOSITE of Split('') Method

    // Join Name 1
    const joinName1 = ['Mr.', firstName, lastName.toUpperCase()]
    console.log(joinName1); // ['Mr.', 'Nur', 'MOHAMOD']
    console.log(...joinName1); // Mr. Nur Mohamod

    // Join Name 2
    const joinName2 = ['Mr.', firstName, lastName.toUpperCase()].join(' ');
    console.log(joinName2); // Mr. Nur MOHAMOD


    // Capitalization Multiple Names or Words
    const capitalizeName = function (name) {
        const names = name.split(' '); // Array output
        console.log(names); // ['nur', 'mohamod', 'rafi']
        console.log(names[1]);
        const namesUpper = [];

        for (const n of names) {
            // Method 1 toUpperCase + Slice
            namesUpper.push(n[0].toUpperCase() + n.slice(1));
            // Method 2 Replace 
            // namesUpper.push(n[0].replace(n[0], n[0].toUpperCase()));
        }
        console.log(namesUpper.join(' '));
    };
    capitalizeName('nur mohamod rafi')

    // Split('') Output = Array // or USE [...]
    // Join Output('') = String

    // Padding

    const message = 'Go to gate 07';
    const padStart = message.padStart(25, '+');
    console.log(padStart.padEnd(35, '+'));
    console.log(padStart.length);


    // Credit Card Masking

    const maskCreditCard = function (number) {
        const str = String(number);
        // OR
        // const str = number + ''// => Number + ''(Empthy String) = String
        const first = str.slice(0, 4); // first 4
        const last = str.slice(-4); // last 4
        return last.padStart(str.length, '*') // Except last 4 digit full length of string will covered by * or any characters

    }
    console.log(maskCreditCard(56869479831659)); // Output: **********1659

    // Phone Number Masking
    const maskPhoneNumber = function (number) {
        const str = String(number);
        // NUMBER to STRING Convert Leading 0 Missing
        const addZero = str.padStart(11, 0); // Leading 0 problem solved
        console.log(addZero);
        const first = addZero.slice(0, 3);
        const last = addZero.slice(-3);
        return first.padEnd(8, '*') + last

    }
    console.log(maskPhoneNumber(01913093140)); // Output: 019*****140

    // Repeat
    const message2 = 'Bed weather... All Departures Delayed...'    

    console.log(message2.repeat(5)); // Repeat 5 times

    const planesInline = function(n){
        console.log(`There are ${n} planes in line ${'✈'.repeat(n)}`);
    };
    planesInline(5) // There are 5 planes in line ✈✈✈✈✈

Coding Challenge
    // ================ //
    // Coding Chellenge //
    // ================ //
    /*
        Write a program that receives a list of variable names written in underscore_case
    and convert them to camelCase.
    The input will come from a textarea inserted into the DOM (see code below to
    insert the elements), and conversion will happen when the button is pressed.
    Test data (pasted to textarea, including spaces):
    underscore_case
    first_name
    Some_Variable
     calculate_AGE
    delayed_departure
    Should produce this output (5 separate console.log outputs):
    underscoreCase ✅
    firstName ✅✅
    someVariable ✅✅✅
    calculateAge ✅✅✅✅
    delayedDeparture ✅✅✅✅✅
    Hints:
    § Remember which character defines a new line in the textarea �
    § The solution only needs to work for a variable made out of 2 words, like a_b
    § Start without worrying about the ✅. Tackle that only after you have the variable
    name conversion working �
    § This challenge is difficult on purpose, so start watching the solution in case
    you're stuck. Then pause and continue!
    */

    /// Solution ///

    document.body.append(document.createElement('textarea'));
    document.body.append(document.createElement('button'));


    document.querySelector('button').addEventListener('click', function () {
        const text = document.querySelector('textarea').value;
        const rows = text.split('\n'); // '\n' = Enter

        for(const [i, row] of rows.entries() ){
            const [first, second] = row.toLowerCase().trim().split('_');
            const output = (`${first}${second[0].toUpperCase()+second.slice('1')}`)
            // toUpperCase() + slice('') or replace(place, item)
            console.log(`${output.padEnd(20, ' ')}${'✅'.repeat(i+1)}`);
        }
    });
    // returns
    // underscore_case => underscoreCase
    // first_name => firstName
    // Some_Variable => someVariable
    //  calculate_AGE => calculateAge
    // delayed_departure => delayedDeparture