Skip to the content.

Arrays

Click ★ if you like the project. Your contributions are heartily ♡ welcome.



SHORT NOTES is provided in this LINK


Declaration

Array has two use cases.

  1. Stack
    1. new elements are added or taken always from the “end”.
    2. A stack is usually illustrated as a pack of cards: new cards are added to the top or taken from the top
    3. LIFO (Last-In-First-Out) principle
      • push adds an element to the end.
      • pop takes an element from the end.
  2. Queue
    1. means an ordered collection of elements which supports two operations:
      1. In practice we need it very often. For example, a queue of messages that need to be shown on-screen.
      2. FIFO (First-In-First-Out).
        • push appends an element to the end.
        • shift get an element from the beginning, advancing the queue, so that the 2nd element becomes the 1st.

          Arrays in JavaScript can work both as a queue and as a stack. They allow you to add/remove elements, both to/from the beginning or the end. In CS called as deque.

Methods pop/push, shift/unshift

let fruits = ["Apple", "Orange", "Pear"];

alert(  fruits.pop()            );          // remove "Pear"                    alert -----> "Pear"
alert(  fruits.push("Pear")     );          // append element to the end        alert -----> "3"
alert(  fruits.shift()          );          // remove "Apple"                   alert -----> "Apple"
alert(  fruits.unshift('Mango') );          // append element to the beginning  alert -----> "3" 

alert(  fruits                  );          // Mango,Orange,Pear

How Arrays works internally

But what makes arrays really special is their internal representation. The engine tries to store its elements in the contiguous memory area, one after another, just as depicted on the illustrations in this chapter, and there are other optimizations as well, to make arrays work really fast. But they all break if we quit working with an array as with an “ordered collection” and start working with it as if it were a regular object.

Please think of arrays as special structures to work with the ordered data. They provide special methods for that. Arrays are carefully tuned inside JavaScript engines to work with contiguous ordered data, please use them this way. If you need arbitrary keys, chances are high that you actually require a regular object {}.

Performance

The shift operation must do 3 things:

The more elements in the array, the more time to move them, more in-memory operations.

The push/pop operation have only one thing to do:

The pop method does not need to move anything, because other elements keep their indexes. That’s why it’s blazingly fast.

Loops

There are three ways to iterate the array:

let arr = ["Apple", "Orange", "Pear"];

// Traditional way
for (let i = 0; i < arr.length; i++) {
  alert( arr[i] );
}

// iterates over array elements
for (let fruit of fruits) {
  alert( fruit );
}


// NOT RECOMMANDATION
for (let key in arr) {
  alert( arr[key] ); // Apple, Orange, Pear
}

But that’s actually a bad idea. There are potential problems with it:

  1. The loop for..in iterates over all properties, not only the numeric ones.
    1. There are so-called “array-like” objects in the browser and in other environments, that look like arrays. That is, they have length and indexes properties, but they may also have other non-numeric properties and methods, which we usually don’t need. The for..in loop will list them though. So if we need to work with array-like objects, then these “extra” properties can become a problem.
  2. The for..in loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it’s still very fast. The speedup may only matter in bottlenecks. But still we should be aware of the difference.

Generally, we shouldn’t use for..in for arrays.

A word about length

lenght is writable

So, the simplest way to clear the array is: arr.length = 0;.

new Array()

let arr = new Array(2); // will it create an array of [2] ?

alert( arr[0] ); // undefined! no elements.

alert( arr.length ); // length 2

Multidimensional arrays

let matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

alert( matrix[0][1] ); // 2, the second value of the first inner array

toString

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true

Don’t compare arrays with ==

Instead you can use for..of loop to compare arrays item-by-item.

Array Methods

arr.splice

Parameter: splice(start, deleteCount, <item1, item2, /* …, */ itemN>)
in-place: YES

let arr = [1, 2, 3, 4, 5];
let removed = arr.splice(2, 2, 6, 7); // Removes 2 elements from starting index 2, adds 6 and 7
// Result: arr = [1, 2, 6, 7, 5],
// returned array: removed = [3, 4]
alert(arr.splice(0));                 // [1, 2, 3, 4, 5] removed all elements
alert(arr);                           // [] empty array

arr = [1, 2, 5];
// from index -1 (one step from the end)
// delete 0 elements,
// then insert 3 and 4
arr.splice(-1, 0, 3, 4);

alert( arr ); // 1,2,3,4,5

arr.slice

Parameter: slice(<start>, <end>)
in-place: NO

let arr = [1, 2, 3, 4, 5];
let result = arr.slice(1, 3); // Extracts elements from index 1 to 3 (not including 3)
// Result: result = [2, 3], arr = [1, 2, 3, 4, 5]

let arrCopy = arr.slice();    // copy of arr, call without arguments 

We can also call it without arguments: arr.slice() creates a copy of arr. That’s often used to obtain a copy for further transformations that should not affect the original array.

arr.concat

Parameter: concat(value1, <value2, /* …, */ valueN>)
in-place: NO

let arr1 = [1, 2];
let arr2 = [3, 4];
let result = arr1.concat(arr2); // Combines arr1 and arr2
// Result: result = [1, 2, 3, 4]

arr.forEach

Parameter: forEach(callbackFn, <thisArg>)
  ♦ callbackFn(element, <index>, <array>)
in-place: NO

let arr = [1, 2, 3];
arr.forEach((num, index) => console.log(`Index ${index}: ${num}`));
// Logs each element and 
// index:
//    "Index 0: 1"
//    "Index 1: 2"
//    ...
//    ...etc.

The result of the function (if it returns any) is thrown away and ignored.

Searching methods in Array

arr.indexOf

let arr = [1, 2, 3, 2];
let first = arr.indexOf(2);    // Result: 1

const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0)

arr.lastIndexOf

Parameter: lastIndexOf(searchElement, <fromIndex>)
in-place: NO

let arr = [1, 2, 3, 2];
let last = arr.lastIndexOf(2); // Result: 3

arr.includes

Parameter: includes(searchElement, <fromIndex>)
in-place: NO

let arr = [1, 2, 3];
alert(arr.includes(2));       // Result: true
alert(arr.includes(2, 1));    // Result: true
alert(arr.includes(2, -1));   // Result: false
alert(arr.includes("2"));     // Result: false

const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0)
alert( arr.includes(NaN) );// true (correct)

arr.find

Parameter: find(callbackFn, <thisArg>)
  ♦ callbackFn(element, <index>, <array>)
in-place: NO

let result = arr.find(function(item, index, array) {
  // if true is returned, item is returned and iteration is stopped
  // for falsy scenario returns undefined
});

let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];

let user = users.find(item => item.id == 1);
let userUndefined = users.find(item => item.id == 4);

alert(user.name);             // John
alert(userUndefined?.name);   // undefined

🙌 You can practice problems from this LINK 😎

arr.findIndex

Parameter: findIndex(callbackFn, <thisArg>)
  ♦ callbackFn(element, <index>, <array>)
in-place: NO

let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"},
  {id: 4, name: "John"}
];

// Find the index of the first John
alert(users.findIndex(user => user.name == 'John')); // 0

You can practice problems from this LINK

arr.findLastIndex

Parameter: findLastIndex(callbackFn, <thisArg>)
  ♦ callbackFn(element, <index>, <array>)
in-place: NO

let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"},
  {id: 4, name: "John"}
];

// Find the index of the last John
alert(users.findLastIndex(user => user.name == 'John')); // 3

arr.filter

Parameter: filter(callbackFn, <thisArg>)
  ♦ callbackFn(element, <index>, <array>)
in-place: NO

// SYNTAX
let results = arr.filter(function(item, index, array) {
  // if true item is pushed to results and the iteration continues
  // returns empty array if nothing found
});

let arr = [1, 2, 3, 4];
let result = arr.filter(num => num % 2 === 0); // Filters even numbers
// Result: result = [2, 4]

Transforming methods in an array

arr.map

Parameter: map(callbackFn, <thisArg>)
  ♦ callbackFn(element, <index>, <array>)
in-place: NO

let arr = [1, 2, 3];
let result = arr.map(num => num * 2); // Multiplies each element by 2
// Result: result = [2, 4, 6]

arr.sort

Parameter: sort(<compareFn>)
  ♦ callbackFn(a, b)
in-place: YES

// items sorted as string -----------------------------> ( 1 )
let arr = [ 1, 2, 15 ];
// the method reorders the content of arr
arr.sort();
alert( arr );  // 1, 15, 2
//"2".codePointAt() is 50 where as "15".codePointAt() is 49. So 50 is greater than 49.

// custom logic func in sorting
function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}
let arr = [ 1, 2, 15,8,21,10,3,4 ];
arr.sort(compareNumeric);
alert(arr);  // 1, 2, 15



let arr = [3, 1, 4, 2];
arr.sort((a, b) => a - b);  // Sorts in ascending order
arr.sort((a, b) => b - a);  // Sorts in descending order
alert(arr);                 // Result: arr = [1, 2, 3, 4]

// Use localeCompare for alphabets: ---------------------------> ( 2 )
let countries = ['Österreich', 'Andorra', 'Vietnam'];
alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (WRONG)
alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (CORRECT!)

🙌 You can practice problems from this LINK 😎


arr.reverse

Parameter: reverse()
in-place: YES

let arr = [1, 2, 3];
arr.reverse();
// Result: arr = [3, 2, 1]

join

let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // glue the array into a string using ;
alert( str ); // Bilbo;Gandalf;Nazgul

arr.reduce

Parameter: reduce(callbackFn, <initialValue>)   ♦ callbackFn(accumulator, currentValue, currentIndex, array).

in-place: NO

// SYNTAX
let value = arr.reduce(function(accumulator, item, index, array) {
  // ...
}, [initial]);


let arr = [1, 2, 3, 4, 5];
let sum = arr.reduce((sum, num) => sum + num, 0); // Sum of all elements
// Result: sum = 15
sum
0
current
1
sum
0+1
current
2
sum
0+1+2
current
3
sum
0+1+2+3
current
4
sum
0+1+2+3+4
current
5
 
1 2 3 4 5 0+1+2+3+4+5 = 15
  sum current result
the first call 0 1 1
the second call 1 2 3
the third call 3 3 6
the fourth call 6 4 10
the fifth call 10 5 15

For more information, please follow this LINK

arr.reduceRight

Parameter: reduceRight(callbackFn, <initialValue>)
in-place: NO

let arr = [1,2,3,4,5];

// Error: Reduce of empty array with no initial value
// if the initial value existed, reduce would return it for the empty arr.
arr.reduceRight((sum, current) => console.log(sum+current , 'and ',current), 0); // 5, 4, 3, 2, 1

Array.isArray

// typeof -------------------------------------------------------> ( 1 )
alert(typeof {}); // object
alert(typeof []); // object (same)


alert(Array.isArray({})); // false
alert(Array.isArray([])); // true

thisArg in Array Methods

let arr = [1, 2, 3];
let multiplier = {
  factor: 2,
};

let result = arr.map(function (num) {
  return num * this.factor;
}, multiplier); // Sets `this` to `multiplier` object
// Result: result = [2, 4, 6]