Simple Array Methods

// Simple array methods
let arr = ['a', 'b', 'c', 'd', 'e'];

// SLICE: Extract part of the array WITHOUT changing the original array (i.e. returns a new array)
console.log(arr.slice(2)); // Prints: ["c", "d", "e"]
console.log(arr.slice(2, 4)); // Prints: ["c", "d"]
console.log(arr.slice(-2)); // Prints: ["d", "e"]
console.log(arr.slice(-1)); // Prints: ["e"]
console.log(arr.slice(1, -2)); // Prints: ["b", "c"]
console.log(arr.slice()); // Prints: ["a", "b", "c", "d", "e"] i.e. returns a shallow copy of the original array
console.log([...arr]); // But this method of creating a shallow copy is much easier

console.log(arr); // Prints: ["a", "b", "c", "d", "e"]. i.e. original array is not mutated in any way


// SPLICE: MUTATES THE ORIGINAL ARRAY. Functionally, it is same as slice().
console.log(arr.splice(2)); // Prints: ["c", "d", "e"]
// But now, notice what happens to our original array. The above extracted elements are basically gone from the original array
console.log(arr); // Prints: ["a", "b"]
// Hence the splice() method is normally used for deleting one or more elements from the array
// One common use case is to remove the last element from an array
console.log(arr.splice(-1)); // Prints: ["b"] // as we have already removed some elements from this array before
console.log(arr); // Prints: ["a"]

// Note that the second args of the splice() method is the number of elements that should be deleted
arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.splice(2, 2)); // Prints: ["c", "d"]
console.log(arr); // Prints: ["a", "b", "e"]


// REVERSE: MUTATES THE ORIGINAL ARRAY. Reverses the array
arr = ['a', 'b', 'c', 'd', 'e'];
const arr2 = ['j', 'i', 'h', 'g', 'f'];
console.log(arr2.reverse()); // Prints: ["f", "g", "h", "i", "j"]
console.log(arr2); // Prints: ["f", "g", "h", "i", "j"]. The original array is mutated


// CONCAT: Join two arrays. Does not change either of the two inout arrays.
const letters = arr.concat(arr2);
console.log(letters); // Prints: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
// But we can also concat two arrays using this:
console.log([...arr, ...arr2]);


// JOIN: join all the elements of an array using the specified separator
console.log(letters.join(' - ')); // Prints: a - b - c - d - e - f - g - h - i - j
console.log(typeof letters.join(' - ')); // Note that the type of the result after join is a string

// Just for the sake of completion - we ahve already looked at these methods earlier

// push: Add elements to the end of the array.
arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.push('f')); // Prints: 6. Because it returns the size of the resulting array
console.log(arr); // Prints: ["a", "b", "c", "d", "e", "f"]

// unshift: Add elements to the beginning of the array.
arr = ['b', 'c', 'd', 'e'];
console.log(arr.unshift('a')); // Prints: 5. Because it returns the size of the resulting array
console.log(arr); // Prints: ["a", "b", "c", "d", "e", "f"]

// pop: Removes the last element of the array. Returns the removed element.
arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.pop()); // Prints: 'e'
console.log(arr); // Prints: ["a", "b", "c", "d"]

// shift: Remove the first element of the array.
arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.shift()); // Prints: 'a'
console.log(arr); // Prints: ["b", "c", "d", "e"]

// indexof: Find the index at which a particular element is at
arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.indexOf('c')); // Prints: 2
console.log(arr.indexOf('f')); // Prints: -1

// includes: Find if the element exists in an array
arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.includes('c')); // Prints: true
console.log(arr.includes('f')); // Prints: false

.at() method

const arr = [23, 11, 64];
console.log(arr[0]); // Prints: 23
console.log(arr.at(0)); // Prints: 23

// getting last array element
console.log(arr[arr.length -1]); // Prints: 64
console.log(arr.slice(-1)[0]); // Prints: 64
console.log(arr.at(-1)); // Prints: 64

// at method also works on stirngs
console.log('jonas'.at(-1)); // Prints: s

forEach : Looping arrays using forEach

"use strict";

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

/* ----------------------- for of ----------------------- */
// We saw earlier how to loop over an iterable using a for-of loop
// And we could access the index in a for-of loop using:
for (const [i, mov] of movements.entries()) {
  if (mov > 0) {
    console.log(`Movement ${i + 1}: You deposited ${mov}`);
  } else {
    console.log(`Movement ${i + 1}: You withdrew ${Math.abs(mov)}`);
  }
}
/* → Movement 1: You deposited 200
     Movement 2: You deposited 450
     Movement 3: You withdrew 400
     ... */

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

/* ---------------------- for each ---------------------- */

// for each fxn is the one which will call the callback function written inside it.
//forEach fxn will loop over the array and in each iteration it will execute the callback function and will pass the current element of the array as an argument.


// basically it'll call the function like this : 
//   0: function(200)
//   1: function(450)
//   2: function(400)



// The callback fn in the forEach method accepts 3 parameters:
// 1) the element of the array itself
// 2) the index of the element
// 3) the entire original array
// Just like in previous method, we can pass in only part of the parameters that are required, and that would work as well

movements.forEach(function (mov, i, arr) {
  if (mov > 0) {
    console.log(`Movement ${i + 1}: You deposited ${mov}`);
  } else {
    console.log(`Movement ${i + 1}: You withdrew ${Math.abs(mov)}`);
  }
});
/* → Movement 1: You deposited 200
     Movement 2: You deposited 450
     Movement 3: You withdrew 400
     ... */

Data Transformations with Map, Filter and Reduce :

Pasted image 20231221221317.png

Map method :

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];


const eurToUsd = 1.1;

const movementsUSD = movements.map(function(mov){
  return mov * eurToUsd;
});
// we can use arrow function to simplify it:
// const movementsUSD = movements.map((mov) =>  mov * eurToUsd);

// original array will remain non mutated
console.log(movements);
console.log(movementsUSD);

/* Prints:  
[
   200,  450, -400,
  3000, -650, -130,
    70, 1300
]
[
  220.00000000000003,
  495.00000000000006,
  -440.00000000000006,
  3300.0000000000005,
  -715.0000000000001,
  -143,
  77,
  1430.0000000000002
]
... */
  • Just like forEach() method the map() method also has access to exact same three parameters : (element, index, entire_array)
  • remember that forEach jab bhi ham use karege to ek ek karke element ayega whereas map() se seedha saare elements combine hoke ek array niklega (see above code block and notice how an entire goddamn array is being returned).

Filter Method :

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

const deposits = movements.filter(function(mov, i) {
  return mov > 0;
})
console.log(deposits); // → [ 200, 450, 3000, 70, 1300 ]

Reduce method :

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// accumulator -> SNOWBALL (basically acc ki value hi ultimately return hoti hai)
const balance = movements.reduce(function(acc, cur, i, arr){
  console.log(`Iteration ${i}: ${acc}`);
  
  return acc + cur;
}, 1); // this zero is the initial value of the accumulator


/* → Iteration 0: 0
Iteration 1: 200
Iteration 2: 650
Iteration 3: 250
Iteration 4: 3250
Iteration 5: 2600
Iteration 6: 2470
Iteration 7: 2540
... */

console.log(balance); // → 3841

for arrow functions we'll write reduce method like this :

  const balance = movements.reduce((acc, mov) => acc + mov, 0);

Reduce Method for calculating largest number in an array :

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];
// to find the largest number in the movements array
const maxTransaction = movements.reduce(function(acc, cur){
  if (acc > cur)
    return acc;
  else
    return cur;
}, movements[0]);

console.log(maxTransaction); // → 3000

Chaining Methods / Pipeline :

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// Suppose we have the following requirement:
// filter out all the -ve values
// convert values to usd
// calculate the sum

// PIPELINE : 
const eurToUsd = 1.1;
const totalDepositsUSD = movements.filter(mov => mov > 0).map(mov => mov * eurToUsd).reduce((acc, mov) => acc + mov, 0);
console.log(totalDepositsUSD); // → 5522.000000000001

// But now if there was an error, how would we debug?
// This is where the use case of passing the original array as an args comes in
// We can rewrite the original function as:

const totalDepositsUSD_debug = movements
  .filter(mov => mov > 0)
  .map((mov, i, arr) => {
    console.log(arr); // this is the resultant array that the .filter method has created
    
    mov * eurToUsd})
  .reduce((acc, mov) => acc + mov, 0);

Find Method :

  • The find method returns the element based on the condition that is specified in the callback function. Note that if multiple elements of the array satisfy the given condition, then in that case the find method will return the first element that satisfies the condition.
const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

const findX = movements.find(mov => mov < 0);
console.log(findX); // → -400

// another case where find operator is really useful is case like this :
const account = accounts.find(acc => acc.owner === 'Jessica Davis');

Find Index :

  • Returns the index of the first element in the array where predicate is true, and -1 otherwise.
    const index = accounts.findIndex(acc => acc.username == currentAccount.username)
  • The difference b/w indexOf and findIndex is that with indexOf we can only search for a value which is in the array, where as with findIndex we can create a complex function to search for an element which satisfies our requirements.

Some and Every Methods :

some Determines whether the specified callback function returns true for any element of an array.
every Determines whether all the members of an array satisfy the specified test.

values = [200, -200, 340, -300, -20, 50, 400, -460];
// We can search whether an element exists or not by using 'includes'
console.log(values.includes(340)); // Prints: true

// some

// The problem with this method is that it only searches for exact matches. We use the 'some' method to search for other conditions
// Suppose we want to check if a particular array contains any positive values or not
console.log(values.some(val => val >= 0)); // Prints: true

// every

// Determines whether all the members of an array satisfy the specified test.
console.log(values.every(amt => amt >= 0)); // Prints: false
values = [200, 340, 50, 400];
console.log(values.every(amt => amt >= 0)); // Prints: true

Flat and FlatMap :

flat Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth.
flatMap Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1. The flatMap method calls the callback function one time for each element in the array.

"use strict";

// Flattens an array : only one level deep
const arr = [[[1, 2], 3], [4, 5, 6], 7, 8];
console.log(arr.flat()); // → [ [ 1, 2 ], 3, 4, 5, 6, 7, 8 ]

// Flatten an array two levels deep
const arrDeep = [[[1, 2], 3], [4, [5, 6]], 7, 8];
console.log(arrDeep.flat(2)); // → (8) [1, 2, 3, 4, 5, 6, 7, 8]

const accounts = [
  {
    owner: "Jonas Schmedtmann",
    movements: [200, 450, -400, 3000, -650, -130, 70, 1300],
    interestRate: 1.2, // %
    pin: 1111,
  },
  {
    owner: "Jessica Davis",
    movements: [5000, 3400, -150, -790, -3210, -1000, 8500, -30],
    interestRate: 1.5,
    pin: 2222,
  },
  {
    owner: "Steven Thomas Williams",
    movements: [200, -200, 340, -300, -20, 50, 400, -460],
    interestRate: 0.7,
    pin: 3333,
  },
];

// Chaining the map and flat methods is common
const overallBalance = accounts
  .map((acc) => acc.movements)
  .flat()
  .reduce((accu, curr) => accu + curr, 0);
  console.log(overallBalance); // → 15570

// The flatMap method is combination between the map and flat methods with better performance
// *flatMap goes only one level deep
const overallBalance2 = accounts
  .flatMap((acc) => acc.movements)
  .reduce((accu, curr) => accu + curr, 0);
  console.log(overallBalance); // → 15570

Sorting :

  • Note that the sort method by default converts everything to strings implicitly. Hence using the sort method to sort arrays of number swill lead to wrong results. Hence be sure to pass in a custom comparator when sorting numbers.
"use strict";

// The sort method mutates the original array
const owners = ["Jonas", "Zach", "Adam", "Martha"];
console.log(owners.sort()); // → (4)[("Adam", "Jonas", "Martha", "Zach")];
console.log(owners); // → (4)[("Adam", "Jonas", "Martha", "Zach")];

// Numbers
const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// return < 0, A, B (keep order)
// return > 0, B, A (switch order)
// Ascending
movements.sort((a, b) => (a > b ? 1 : -1)); // see above 3 lines for explaination
console.log(movements); // → (8) [-650, -400, -130, 70, 200, 450, 1300, 3000]
movements.sort((a, b) => a - b);
console.log(movements); // → (8) [-650, -400, -130, 70, 200, 450, 1300, 3000]

// Descending
movements.sort((a, b) => b - a);
console.log(movements); // → (8) [3000, 1300, 450, 200, 70, -130, -400, -650]