JavaScript Course 5: Arrays

From WikiMLT
Revision as of 08:28, 26 September 2022 by Spas (talk | contribs) (Text replacement - "mlw-continue" to "code-continue")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Ref­er­ences

See al­so:

De­clar­ing an Ar­ray

const numbers = [3, 4];  // Array Literal
typeof numbers;          // "object"

Con­stant does not stop us from mod­i­fy­ing the con­tent of an Ar­ray, we can adding and re­move el­e­ments. Just we can't re­as­sign the vari­able to dif­fer­ent type.

In JavaScript, the Ar­rays are kind of ob­jects, so the are a cou­ple of built-in prop­er­ties [like .length] and meth­ods [like .in­dex­Of()].

Re­mem­ber the in­dex of the Ar­rays starts from 0.

[1, 2, 3, 1, 4]
 0  1  2  3  4

Adding El­e­ments to an Ar­ray

Add el­e­ments to the end of an Ar­ray by us­ing .push(elements) method. It adds new items to the end of an ar­ray, changes the length of the ar­ray, re­turns the new length.

numbers.push(5, 6);
console.log(numbers);
(4) [3, 4, 5, 6]

Add el­e­ments to the be­gin­ning of an Ar­ray by us­ing .unshift(elements) method. It adds new el­e­ments to the be­gin­ning of an ar­ray, over­writes the orig­i­nal ar­ray.

numbers.unshift(1, 2);
console.log(numbers);
(6) [1, 2, 3, 4, 5, 6]

Add el­e­ments to the mid­dle of an Ar­ray by us­ing .splice(index, delete count, el­e­ments to add) method. It adds and/​​​or re­moves ar­ray el­e­ments, over­writes the orig­i­nal ar­ray.

numbers.splice(2, 0, 'a', 'b'); // 0 means we won't remove elements
console.log(numbers);
(8) [1, 2, 'a', 'b', 3, 4, 5, 6]

Find­ing El­e­ments in an Ar­ray

Find­ing Prim­i­tive types in an Ar­ray

const numbers = [1, 2, 3, 1, 4];

By us­ing .indexOf(element, start_index=0) method we can find the first oc­cur­rence of an el­e­ment.

console.log(numbers.indexOf(1));
0
console.log(numbers.indexOf(3));
2
console.log(numbers.indexOf('c')); // This element doesn't exist
-1

By us­ing .lastIndexOf(element, start_index=array_lenght) method we can find the last oc­cur­rence of an el­e­ment.

console.log(numbers.latIndexOf(1));
3

Test whether a giv­en (prim­i­tive) el­e­ment ex­ists in an Ar­ray

const numbers = [1, 2, 3, 1, 4];

The old way, by us­ing .indexOf(element, start_index=0) method.

console.log(numbers.indexOf(1) !== -1); // will return 'true' if one or more elements with value 1 exist in the array 'numbers'
if (numbers.indexOf(1) !== -1) { ... }

The mod­ern way, by us­ing .includes(element, start_index=0) method.

console.log(numbers.includes(1)); // will return 'true' if one or more elements with value 1 exist in the array 'numbers'
if (numbers.includes(1)) { ... }

Start In­dex

All these meth­ods ah a sec­ond op­tion­al pa­ra­me­ter which is start in­dex.

const numbers = [1, 2, 3, 1, 4, 1, 5, 6];
console.log(numbers.length);
8
console.log(numbers.indexOf(1)); // start index = 0
0
console.log(numbers.indexOf(1, 2)); // start index = 2
3
console.log(numbers.lastIndexOf(1)); // start index = array_length, and backwods count
5
console.log(numbers.lastIndexOf(1, 4)); // start index = 4, and backwods count
3

Find­ing Ref­er­ence types in an Ar­ray

const courses = [
    {id: 1, name: 'a'},
    {id: 2, name: 'b'}
];

By us­ing .find(callback_fn) method we can find an Ob­ject with­in Ar­ray, which has a giv­en prop­er­ty.

let course_A_exist = courses.find(function(course) {
   return course.name === 'a'; 
});
console.log(course_A_exist);  // will return 'undefined' if the element dosn't exist
{id: 1, name: 'a'}

By us­ing .findIndex(callback_fn) method we can find the In­dex of an Ob­ject with­in Ar­ray, which has a giv­en prop­er­ty.

let course_B_index = courses.findIndex(function(course) {
   return course.name === 'b'; 
});
console.log(course_B_index);  // will return '-1' if the element dosn't exist
1

Ar­row Func­tions

Here is how to trans­form the above call­back func­tion to an Ar­row func­tion.

The ini­tial state of the call­back func­tion.

#Init state
let course_A_exist = courses.find(function(course) {
   return course.name === 'a'; 
});

Step 1, re­move the func­tion key­word, and sep­a­rate the pa­ra­me­ters of the func­tion from its body put a fat ar­row.

#Step 1
let course_A_exist = courses.find((course) => {
   return course.name === 'a'; 
});

Step 2, if the func­tion have a sin­gle pa­ra­me­ter we can al­so re­move the paren­the­ses.

#Step 2
let course_A_exist = courses.find(course => {
   return course.name === 'a'; 
});

If you do not have any pa­ra­me­ters you have to pass emp­ty paren­the­ses:

let course_A_exist = courses.find(() => { ... });

Step 3, if the func­tion is a sin­gle line of code, we can make this code even short­er: 1) get rid of the re­turn key­word, 2) re­move the curly braces, 3) place every­thing on one line.

#Rslt
let course_A_exist = courses.find(course => course.name === 'a');

Re­mov­ing El­e­ments of an Ar­ray

const numbers = [1, 2, 3, 4, 5, 6];

Re­move el­e­ments to the end of an Ar­ray by us­ing .pop(elements) method. It re­moves (pops) the last el­e­ment of an ar­ray, changes the orig­i­nal ar­ray, re­turns the re­moved el­e­ment.

let last = numbers.pop();
console.log(numbers);
(5) [1, 2, 3, 4, 5]
console.log(last);
6

Re­move el­e­ments to the be­gin­ning of an Ar­ray by us­ing .shift(elements) method. It re­moves the first item of an ar­ray, changes the orig­i­nal ar­ray, re­turns the shift­ed el­e­ment.

let first = numbers.shift();
console.log(numbers);
(4) [2, 3, 4, 5]
console.log(first);
1

Re­move el­e­ments to the mid­dle of an Ar­ray by us­ing .splice(index, delete count, el­e­ments to add) method. It adds and/​​​or re­moves ar­ray el­e­ments, over­writes the orig­i­nal ar­ray.

numbers.splice(1, 2); // we will remove 2 elemets starting from index 1
console.log(numbers);
(2) [2, 5]

Emp­ty­ing an Ar­ray

1. Re­as­sign to a new emp­ty ar­ray. In this case we can't use con­st in or­der to de­fine the array's mem­o­ry ob­ject.

let numbers = [1, 2, 3, 4, 5, 6];
numbers = [];
console.log(numbers);
[]

Note, the old ar­ray ex­ists as an ob­ject in the mem­o­ry and if it is not used any­more, it will be han­dled by the garbage col­lec­tor. But if we have its ref­er­ence we sill able to see and use it.

let numbers = [1, 2, 3, 4, 5, 6];
let another = numbers;
numbers = [];
console.log(numbers);
[]
console.log(another);
(6) [1, 2, 3, 4, 5, 6]

So the above so­lu­tion works if do not have ref­er­ences to the orig­i­nal ar­ray. Oth­er­wise if you do have mul­ti­ple ref­er­ences to the orig­i­nal ar­ray an you want to change (emp­ty) this ar­ray you have to use a dif­fer­ent so­lu­tion.

2. Change the ar­ray .length prop­er­ty to 0. In this case we can use con­st in or­der to de­fine the ar­ray mem­o­ry ob­ject. This is the best so­lu­tion!

const numbers = [1, 2, 3, 4, 5, 6];
const another = numbers;
numbers.length = 0;
console.log(numbers, another);
[] []

3. Use the .splice() method. In this case we can use con­st in or­der to de­fine the ar­ray mem­o­ry ob­ject.

const numbers = [1, 2, 3, 4, 5, 6];
const another = numbers;
numbers.splice(0, numbers.length);
console.log(numbers, another);
[] []

4. Use the .pop() method and a loop. In this case we can use con­st in or­der to de­fine the ar­ray mem­o­ry ob­ject. This is not rec­om­mend­ed, per­for­mance cost so­lu­tion.

const numbers = [1, 2, 3, 4, 5, 6];
const another = numbers;
while (numbers.length > 0) numbers.pop();
console.log(numbers, another);
[] []

Com­bin­ing and Slic­ing Ar­rays

Com­bin­ing Ar­rays

To com­bine these Ar­rays we can use the .concat(array) method.

const first = [1, 2, 3];
const second = [4, 5, 6];
const combined = first.concat(second);
console.log(combined);
(6) [1, 2, 3, 4, 5, 6]

Slic­ing an Ar­ray

const combined = [1, 2, 3, 4, 5, 6];

In or­der do slice an Ar­ray we can use the .slice(start in­dex, end in­dex) method.

const slice1 = combined.slice(2, 4);
console.log(slice1);
(2) [3, 4]

We can use al­so on­ly the start in­dex.

const slice2 = combined.slice(3);
console.log(slice2);
(3) [4, 5, 6]

If we omit al­so the start in­dex we can cre­ate a copy of the ar­ray by this method.

const copy = combined.slice();
console.log(copy);
(6) [1, 2, 3, 4, 5, 6]

Ref­er­ence types

The above meth­ods – .slice() and .con­cat() – are deal­ing with Prim­i­tive types. If we have Ref­er­ence types in an Ar­ray these meth­ods will not copy the re­ferred ob­jects, but on­ly the ref­er­ences to them.

const first = [{id: 1}, {id: 2}];
const second = [4, 5, 6];
const combined = first.concat(second);
first[0].id = 888;
console.log(combined);
(5) [{id: 888}, {id: 2}, 4, 5, 6]

The Spread Op­er­a­tor

const first = [1, 2, 3];
const second = [4, 5, 6];

In ES6 we can use the Spread op­er­a­tor – ...spread – to achieve the same re­sult as the .con­cat() method from the above sec­tion. The Spread op­er­a­tor ap­proach is clean­er and more flex­i­ble.

const combined1 = [...first, ...second];
console.log(combined1);
(6) [1, 2, 3, 4, 5, 6]

By us­ing the Spread method we can add el­e­ments in a way as it is shown be­low.

const combined2 = ['at the beginning', ...first, 'in the middle', ...second, 'at the end'];
console.log(combined2);
(9) ['at the beginning', 1, 2, 3, 'in the middle', 4, 5, 6, 'at the end']

We can use the Spread op­er­a­tor al­so in or­der to copy an ar­ray.

const copyOfFirst = [...first];
console.log(copyOfFirst);
(3) [1, 2, 3]

The spread op­er­a­tor al­so deal­ing on­ly with Pro­mo­tive types and just copies the ref­er­ences of Ref­er­ence types.

It­er­at­ing an Ar­ray

const numbers = [1, 2, 3];

Use for.. of.. loop.

for (let number in numbers) console.log(number);
#Out­put
1
2
3

Use .forEach(calback_f) method.

numbers.forEach(function(number) { console.log(number) });
#Out­put
1
2
3
numbers.forEach(number => console.log(number));
#Out­put
1
2
3

The loop­back func­tion can have sec­ond pa­ra­me­ter, which is in­dex.

numbers.forEach((element, index) => console.log(index, element));
#Out­put
0 1
1 2
2 3

Join­ing an Ar­ray and Split­ting a String

The .join('optional field separator') method could be used to con­vert an Ar­ray in­to string.

const numbers = [1, 2, 3, 4, 5, 6];
const joined = numbers.join('-');
console.log(joined);
1-2-3-4-5-6

We can use the .split('field separator') method (that be­longs to the String ob­jects) in or­der to con­vert a String to an Ar­ray.

const text = 'This is the message';
const array = text.split(' ');
console.log(array);
(4) ['This', 'is', 'the', 'message']

Sort­ing and Re­verse Ar­rays

Prim­i­tive types

const numbers = [2, 3, 1];

Use .sort() method. It sorts the el­e­ments of an ar­ray, over­writes the orig­i­nal ar­ray, sorts the el­e­ments as strings in al­pha­bet­i­cal and as­cend­ing or­der.

numbers.sort();
console.log(numbers);
(3) [1, 2, 3]

Use .re­verse() method. The ar­ray af­ter it has been re­versed.

numbers.reverse();
console.log(numbers);
(3) [3, 2, 1]

Ref­er­ence types

const numbers = [
    {id: 1, name: 'Node.js'},   // When sorting by name this should be Third
    {id: 3, name: 'C++'},       // When sorting by name this should be First
    {id: 2, name: 'JavaScript'} // When sorting by name this should be Second
];

Use .sort(callback_fn) method. There the sort­ing will be dove via ASCII or­der, ref­er­ence: https://​www​.asciitable​.com/

numbers.sort(function(a, b) {
    // Remove case sensitivity
    const nameA = a.name.toLowerCase(); // .toUpperCase() is also valid,
    const nameB = b.name.toLowerCase(); // but both should be equal
    
    if (nameA < nameB) return -1;
    if (nameA > nameB) return 1;
    return 0;
});
console.log(numbers);
(3) [{id: 3, name: 'C++'}, {id: 2, name: 'JavaScript'}, {id: 1, name: 'Node.js'}]

Test­ing the El­e­ments of an Ar­ray

const numbers = [2, 3, 1];

Use .every(callback_fn) method. It ex­e­cutes a func­tion for each ar­ray el­e­ment, re­turns true if the func­tion re­turns true for all el­e­ments, re­turns false if the func­tion re­turns false for one el­e­ment, does not ex­e­cute the func­tion for emp­ty el­e­ments, does not change the orig­i­nal ar­ray. Avail­able in ES6+.

let areAllPositive = numbers.every(function(value) {
    return value >= 0;
});
console.log(areAllPositive);
true
numbers[3] = -2;
areAllPositive = numbers.every(value => value >= 0);
console.log(areAllPositive);
false

Use .some(callback_fn) method. It checks if any ar­ray el­e­ments pass a test (pro­vid­ed as a func­tion); ex­e­cutes the func­tion once for each ar­ray el­e­ment: 1) If the func­tion re­turns true, some() re­turns true and stops, 2) If the func­tion re­turns false, some() re­turns false and stops; does not ex­e­cute the func­tion for emp­ty ar­ray el­e­ments; does not change the orig­i­nal ar­ray.

let atLeastOneNegative = numbers.some(function(value) {
    return value < 0;
});
console.log(atLeastOnePositive);
true
numbers.pop();
atLeastOneNegative = numbers.some(value => value < 0);
console.log(atLeastOneNegative);
false

Fil­ter­ing an Ar­ray

const numbers = [2, -1, 3, -7, 1, 5];

Use .filter(callback_fn) in or­der to fil­ter an ar­ray based on a search cri­te­ria. It cre­ates a new ar­ray filled with el­e­ments that pass a test pro­vid­ed by a func­tion, does not ex­e­cute the func­tion for emp­ty el­e­ments, does not change the orig­i­nal ar­ray. In the next ex­am­ple we will fil­ter on­ly the pos­i­tive val­ues. And in ad­di­tion will sort them in a re­verse or­der.

let onlyPositive = numbers.filter(function(value) {
    return value >= 0;
}).sort().reverse();
  • Bet­ter prac­tice is to put chain­ing meth­ods on a new line.
console.log(onlyPositive);
(4) [5, 3, 2, 1]

Map­ping an Ar­ray

const items = ['item 1', 'item 2', 'item 3'];

With the .map(callback_fn) method we can map each el­e­ment in an Ar­ray to some­thing else. It cre­ates a new ar­ray from call­ing a func­tion for every ar­ray el­e­ment, calls a func­tion once for each el­e­ment in an ar­ray, does not ex­e­cute the func­tion for emp­ty el­e­ments, does not change the orig­i­nal ar­ray.

const itemsList = '<ul>' + items.map(item => '<li>' + item + '</li>').join('') + '</ul>';  // Note ',' is the default joining separator of .join()
console.log(itemsList);
<ul><li>item 1</li><li>item 2</li><li>item 3</li></ul>

Con­vert an Ar­ray of Prim­i­tives in­to an Ar­ray of Ref­er­ences

This ap­proach should be avail­able with all kind of (call­back) func­tions – here we will use the .map() method.

const numbers = [1, 2, 3];

Us­ing a Stan­dard func­tion and ex­plic­it syn­tax.

const items = numbers.map(function(n) {
    const obj = {'value': n};
    return obj;
});

Us­ing a Stan­dard func­tion and short syn­tax.

const items = numbers.map(function(n) {
    return {'value': n};
});

Us­ing an Ar­row func­tion. Note in this case we need to en­close the out­put ob­ject in paren­the­sis, be­cause in oth­er­wise the JavaScript en­gine will threat the curly braces as code block.

const items = numbers.map(n => ({'value': n}) );

The out­put of the three ex­am­ples is the same.

console.log(items);
(3) [{value: '1'}, {value: '2'}, {value: '3'}]

Re­duc­ing an Ar­ray

const numbers = [1, -1, 2, 3];

Us­ing for.. of.. loop – the old way.

let sum = 0;
for (let n of numbers) sum += n;
console.log(sum);
5

Us­ing .reduce(function(total, cur­rent­Val­ue, cur­rentIn­dex, arr), ini­tial­Val­ue) method of the ar­ray ob­jects. It ex­e­cutes a re­duc­er func­tion for ar­ray el­e­ment, re­turns a sin­gle val­ue: the function's ac­cu­mu­lat­ed re­sult, does not ex­e­cute the func­tion for emp­ty ar­ray el­e­ments, does not change the orig­i­nal ar­ray.

let sum = numbers.reduce(function(accumulator, currentValue) {
    return accumulator + currentValue;
}, 0); // accumulator = 0; if we omit this value the 'accumulator' will be initialized with the value of the firs element

Or as Ar­row func­tion and with­out ini­tal­Val­ue for the to­tal.

let sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue);
console.log(sum);
5