`Array`-object

Dit artikel legt het Array-object uit.

Ik leg het praktische gebruik van arrays stap voor stap op een begrijpelijke manier uit.

YouTube Video

Array-object

Array is een van de belangrijkste structuren in JavaScript en vormt de basis van allerlei soorten gegevensverwerking. Van basisbewerkingen tot hogere-orde-functies die nuttig zijn voor efficiënte datatransformatie, er zijn veel functies die je moet kennen.

Array Basisprincipes

In JavaScript zijn arrays een fundamentele gegevensstructuur om meerdere waarden samen te verwerken. Hier laten we zien hoe je arrays maakt en hoe je hun elementen kunt lezen en schrijven met eenvoudige voorbeelden.

 1// Create arrays in different ways
 2const arr1 = [1, 2, 3];           // array literal
 3const arr2 = new Array(4, 5, 6);  // Array constructor
 4const arr3 = Array.of(7, 8, 9);   // Array.of
 5
 6console.log("arr1 created with literal.   :", arr1);
 7console.log("arr2 created with constructor:", arr2);
 8console.log("arr3 created with Array.of.  :", arr3);
 9// arr1 created with literal.   : [ 1, 2, 3 ]
10// arr2 created with constructor: [ 4, 5, 6 ]
11// arr3 created with Array.of.  : [ 7, 8, 9 ]
12
13// Access and modify elements
14let first = arr1[0];              // read element
15console.log("First element of arr1:", first);
16// First element of arr1: 1
17
18arr1[1] = 20;                     // modify element
19console.log("arr1 after modifying index 1:", arr1);
20// arr1 after modifying index 1: [ 1, 20, 3 ]
21
22const len = arr1.length;          // get length
23console.log("Length of arr1:", len);
24// Length of arr1: 3
  • Deze code laat drie manieren zien om arrays te maken, hoe je elementen leest en bijwerkt via indexen, en hoe je de lengte met de length-eigenschap krijgt.
  • Array-literals zijn het meest voorkomend en leesbaar, en worden het vaakst gebruikt in alledaagse situaties.

Elementen toevoegen en verwijderen (aan het begin of einde)

Met arrays kun je eenvoudig elementen toevoegen of verwijderen aan het begin of einde. Deze bewerkingen zijn ook handig bij het implementeren van structuren zoals stacks of queues.

 1// Push and pop (stack-like)
 2const stack = [];
 3console.log("Initial stack:", stack);
 4
 5stack.push(1);                    // push 1
 6console.log("After push(1):", stack);
 7
 8stack.push(2);                    // push 2
 9console.log("After push(2):", stack);
10
11const last = stack.pop();         // pop -> 2
12console.log("Popped value:", last);
13console.log("Stack after pop():", stack);
14
15// Unshift and shift (queue-like)
16const queue = [];
17console.log("Initial queue:", queue);
18
19queue.push('a');                  // add to end
20console.log("After push('a'):", queue);
21
22queue.unshift('start');           // add to front
23console.log("After unshift('start'):", queue);
24
25const firstItem = queue.shift();  // remove from front
26console.log("Shifted value:", firstItem);
27console.log("Queue after shift():", queue);
28
29// Initial stack: []
30// After push(1): [ 1 ]
31// After push(2): [ 1, 2 ]
32// Popped value: 2
33// Stack after pop(): [ 1 ]
34
35// Initial queue: []
36// After push('a'): [ 'a' ]
37// After unshift('start'): [ 'start', 'a' ]
38// Shifted value: start
39// Queue after shift(): [ 'a' ]
  • push en pop werken op het einde van een array. Ze zijn ideaal voor het implementeren van stack-structuren.
  • unshift en shift werken op het begin van een array. Wees er echter van bewust dat bewerkingen aan het begin alle indexen intern verschuiven, wat het een dure operatie maakt.

Elementen in het midden bewerken (splice en slice)

Kies, wanneer je elementen in het midden van een array bewerkt, tussen splice en slice afhankelijk van of je het originele array wilt aanpassen. Als je alleen een deel van een array wilt extraheren, gebruik dan slice; als je de array zelf wilt wijzigen, bijvoorbeeld door elementen toe te voegen of te verwijderen, gebruik dan splice.

 1// slice (non-destructive)
 2const nums = [0, 1, 2, 3, 4];
 3console.log("Original nums:", nums);
 4
 5const part = nums.slice(1, 4); // returns [1, 2, 3]
 6console.log("Result of nums.slice(1, 4):", part);
 7console.log("nums after slice (unchanged):", nums);
 8
 9// splice (destructive)
10const arr = [10, 20, 30, 40];
11console.log("\nOriginal arr:", arr);
12
13// remove 1 item at index 2, insert 25 and 27
14arr.splice(2, 1, 25, 27);
15console.log("After arr.splice(2, 1, 25, 27):", arr);
16
17// Original nums: [ 0, 1, 2, 3, 4 ]
18// Result of nums.slice(1, 4): [ 1, 2, 3 ]
19// nums after slice (unchanged): [ 0, 1, 2, 3, 4 ]
20
21// Original arr: [ 10, 20, 30, 40 ]
22// After arr.splice(2, 1, 25, 27): [ 10, 20, 25, 27, 40 ]
  • slice haalt alleen elementen op en past het originele array niet aan.
  • splice voegt elementen toe of verwijdert ze en past het array zelf aan, let dus goed op het effect op het gedrag.

Iteratie (for / for...of / forEach)

Er zijn verschillende manieren om arrays achtereenvolgens te verwerken; kies wat bij je doel en programmeerstijl past. Hier zijn drie veelgebruikte loop-constructies.

 1const items = ['apple', 'banana', 'cherry'];
 2console.log("Items:", items);
 3
 4// classic for
 5console.log("\n--- Classic for loop ---");
 6for (let i = 0; i < items.length; i++) {
 7  console.log(`Index: ${i}, Value: ${items[i]}`);
 8}
 9
10// for...of (recommended for values)
11console.log("\n--- for...of loop ---");
12for (const item of items) {
13  console.log(`Value: ${item}`);
14}
15
16// forEach (functional style)
17console.log("\n--- forEach loop ---");
18items.forEach((item, index) => {
19  console.log(`Index: ${index}, Value: ${item}`);
20});
  • De for-lus is het meest flexibel, biedt indexbewerkingen en fijne iteratiecontrole via bijvoorbeeld break.
  • for...of biedt een beknopte manier om elementwaarden te verwerken en is qua leesbaarheid het meest in balans.
  • forEach maakt functionele code mogelijk en is vooral geschikt voor bewerkingen met bijwerkingen, zoals loggen of bijwerken. Let wel op dat je geen break of continue kunt gebruiken, en het is niet geschikt voor asynchrone verwerking met await.

map / filter / reduce — Hogere-orde functies

map, filter en reduce zijn hogere-orde functies die vaak worden gebruikt bij het transformeren, filteren of aggregeren van arrays.`. Omdat je herhalende bewerkingen duidelijk kunt uitdrukken, wordt je code eenvoudig en gemakkelijk te begrijpen.

 1const numbers = [1, 2, 3, 4, 5];
 2console.log("Original numbers:", numbers);
 3
 4// map: transform each item
 5const doubled = numbers.map(n => n * 2);
 6console.log("\nResult of map (n * 2):", doubled);
 7
 8// filter: select items
 9const evens = numbers.filter(n => n % 2 === 0);
10console.log("Result of filter (even numbers):", evens);
11
12// reduce: accumulate to single value
13const sum = numbers.reduce((acc, n) => acc + n, 0);
14console.log("Result of reduce (sum):", sum);
15
16// Original numbers: [ 1, 2, 3, 4, 5 ]
17// Result of map (n * 2): [ 2, 4, 6, 8, 10 ]
18// Result of filter (even numbers): [ 2, 4 ]
19// Result of reduce (sum): 15
  • Met deze methoden kun je je richten op wat je wilt bereiken in een declaratieve stijl, wat de leesbaarheid vergroot en ongewenste bijwerkingen voorkomt.

find / findIndex / some / every

Hier volgt een overzicht van zoek- en conditiecontrole-methoden. Deze zijn handig om elementen te zoeken die aan bepaalde voorwaarden voldoen of om booleaanse checks op de set uit te voeren.

 1const users = [
 2  { id: 1, name: 'Alice' },
 3  { id: 2, name: 'Bob' },
 4  { id: 3, name: 'Carol' }
 5];
 6
 7console.log("Users:", users);
 8
 9// Find the first user whose name is 'Bob'
10const bob = users.find(user => user.name === 'Bob');
11console.log("\nResult of find (name === 'Bob'):", bob);
12
13// Find index of the user whose id is 3
14const indexOfId3 = users.findIndex(user => user.id === 3);
15console.log("Result of findIndex (id === 3):", indexOfId3);
16
17// Check if there exists a user with id = 2
18const hasId2 = users.some(user => user.id === 2);
19console.log("Result of some (id === 2):", hasId2);
20
21// Check if all users have a numeric id
22const allHaveNumericId = users.every(user => typeof user.id === 'number');
23console.log("Result of every (id is number):", allHaveNumericId);
24// Result of find (name === 'Bob'): { id: 2, name: 'Bob' }
25// Result of findIndex (id === 3): 2
26// Result of some (id === 2): true
27// Result of every (id is number): true
  • find retourneert het eerste element dat voldoet aan de conditie.
  • findIndex geeft de index terug van het element dat aan de conditie voldoet.
  • some retourneert true als er minstens één element is dat aan de conditie voldoet.
  • every retourneert true als alle elementen aan de conditie voldoen.

Al deze zijn erg handig bij array-verwerking; ze juist toepassen maakt je code beknopt en duidelijk.

Sorteren en Vergelijkingsfuncties

Arrays worden gesorteerd met sort, maar standaard vergeleken als strings, wat onverwachte resultaten geeft bij het sorteren van getallen.

 1const nums = [10, 2, 33, 4];
 2console.log("Original nums:", nums);
 3
 4// Default sort: compares elements as strings (not suitable for numbers)
 5nums.sort();
 6console.log("\nAfter default sort (string comparison):", nums);
 7
 8// Numeric ascending sort using a compare function
 9nums.sort((a, b) => a - b);
10console.log("After numeric sort (a - b):", nums);
11
12// Sort objects by a property
13const people = [{ age: 30 }, { age: 20 }, { age: 25 }];
14console.log("\nOriginal people:", people);
15
16people.sort((a, b) => a.age - b.age);
17console.log("After sorting people by age:", people);
18
19// After default sort (string comparison): [ 10, 2, 33, 4 ]
20// After numeric sort (a - b): [ 2, 4, 10, 33 ]
21// Original people: [ { age: 30 }, { age: 20 }, { age: 25 } ]
22// After sorting people by age: [ { age: 20 }, { age: 25 }, { age: 30 } ]
  • Als je getallen of objecten sorteert, specificeer dan altijd een vergelijkingsfunctie om de bedoelde volgorde te krijgen.
  • Bij een vergelijkingsfunctie zorgt een negatief resultaat ervoor dat a vóór b komt, een positief dat b vóór a komt, en 0 dat de volgorde gelijk blijft.

Array-kopiëren en Onveranderlijkheid

Bij het kopiëren van arrays is het belangrijk om het verschil tussen een 'referentiekopie' en een 'oppervlakkige kopie' te begrijpen. Let er vooral op dat als er objecten in het array zitten, een oppervlakkige kopie (shallow copy) de interne objecten deelt.

Referentiekopie

Wanneer je een array aan een andere variabele toewijst, wordt de array zelf niet gedupliceerd; in plaats daarvan wordt de 'referentie' die naar dezelfde array wijst, gekopieerd.

 1const a = [1, 2, 3];
 2console.log("Original a:", a);
 3
 4// Reference copy; modifying b also affects a
 5const b = a;
 6console.log("\nReference copy b = a:", b);
 7// Reference copy b = a: [ 1, 2, 3 ]
 8
 9b[0] = 100;
10console.log("After modifying b[0] = 100:");
11console.log("a:", a);  // a: [ 100, 2, 3 ] (affected)
12console.log("b:", b);  // b: [ 100, 2, 3 ]
  • Bij een referentiekopie worden wijzigingen die je met de gekopieerde variabele aanbrengt in de array ook doorgevoerd in de originele variabele die naar dezelfde array verwijst.

Oppervlakkige kopie

Met slice() of de spread-syntax maak je een 'oppervlakkige kopie' omdat alleen de waarden worden gekopieerd; de originele en gekopieerde arrays worden als aparte entiteiten behandeld.

 1const a = [1, 2, 3];
 2console.log("Original a:", a);
 3
 4// Shallow copy (new array)
 5const b = a.slice();
 6console.log("\nShallow copy using slice():", b);
 7// Shallow copy using slice(): [ 100, 2, 3 ]
 8
 9const c = [...a];
10console.log("Shallow copy using spread [...a]:", c);
11// Shallow copy using spread [...a]: [ 100, 2, 3 ]
12
13// Modifying c or d does NOT affect a
14b[1] = 200;
15c[2] = 300;
16console.log("\nAfter modifying b[1] = 200 and c[2] = 300:");
17console.log("a:", a);   // [ 100, 2, 3 ]
18console.log("b:", b);   // [ 100, 200, 3 ]
19console.log("c:", c);   // [ 100, 2, 300 ]
  • Deze code laat zien dat een oppervlakkige kopie van een array, gemaakt met slice() of de spread-syntax, geen effect heeft op de originele array.

Oppervlakkige kopie en onveranderlijkheid

Zelfs als je een array dupliceert met een 'oppervlakkige kopie', kan onbedoelde deling optreden als de array objecten bevat.

 1// Shallow copy doesn't clone inner objects
 2const nested = [{ x: 1 }, { x: 2 }];
 3console.log("\nOriginal nested:", nested);
 4// Original nested: [ { x: 1 }, { x: 2 } ]
 5
 6const shallow = nested.slice();
 7console.log("Shallow copy of nested:", shallow);
 8// Shallow copy of nested: [ { x: 1 }, { x: 2 } ]
 9
10// Changing inner object affects both arrays
11shallow[0].x = 99;
12console.log("\nAfter shallow[0].x = 99:");
13console.log("nested:", nested);
14console.log("shallow:", shallow);
15// nested: [ { x: 99 }, { x: 2 } ]
16// shallow: [ { x: 99 }, { x: 2 } ]
  • Deze code laat zien dat bij een oppervlakkige kopie de innerlijke objecten gedeeld worden, waardoor het aanpassen van deze objecten zowel de originele array als de kopie beïnvloedt.
  • Als je onafhankelijke data nodig hebt, maak dan een 'deep copy', bijvoorbeeld door structuredClone() of omzetting naar JSON te gebruiken.

Handige hulpfuncties

Hieronder volgen hulpmethoden die vaak gebruikt worden bij het werken met arrays. Door deze goed voor je doel in te zetten, kun je korte en leesbare code schrijven.

includes

De includes-methode controleert of een specifieke waarde voorkomt in een array.

1// includes: check if array contains a value
2const letters = ['a', 'b', 'c'];
3const hasB = letters.includes('b');
4console.log("letters:", letters);  // [ 'a', 'b', 'c' ]
5console.log("letters.includes('b'):", hasB); // true
  • In deze code wordt de includes-methode gebruikt om beknopt te bepalen of een opgegeven waarde in de array voorkomt.

concat

De concat-methode retourneert een nieuwe array waarbij de opgegeven array of waarden aan het einde worden toegevoegd, terwijl de originele array onveranderd blijft.

1// concat: merge arrays without mutation
2const a1 = [1, 2];
3const a2 = [3, 4];
4const combined = a1.concat(a2);
5console.log("a1.concat(a2):", combined); // [ 1, 2, 3, 4 ]
6console.log("a1(unchanged):", a1);       // [ 1, 2 ]
  • Deze code laat zien dat concat niet-destructief is, waardoor je een nieuwe array kunt maken terwijl de originele behouden blijft.

flat

Met de flat-methode kun je geneste arrays vlak maken (flattenen).

1// flat and flatMap: flatten arrays or map + flatten in one step
2const nested = [1, [2, [3]]];
3console.log("nested:", nested); // nested: [ 1, [ 2, [ 3 ] ] ]
4console.log("nested.flat():", nested.flat()); // default depth = 1
5// nested.flat(): [ 1, 2, [ 3 ] ]
  • Deze code toont het resultaat van het vlak maken van een array met één niveau.
  • Omdat je bij flat de diepte kunt aangeven, kun je flexibel de geneste niveaus oplossen zoals nodig.

flatMap

De flatMap-methode past een transformatie toe op elk element en maakt het resultaat automatisch vlak tot een eendimensionale array.

1// flat and flatMap: flatten arrays or map + flatten in one step
2const words = ['hello world', 'hi'];
3console.log("words:", words); // [ 'hello world', 'hi' ]
4
5const splitWords = words.flatMap(w => w.split(' '));
6console.log("words.flatMap(w => w.split(' ')):", splitWords);
7// words.flatMap(w => w.split(' ')): [ 'hello', 'world', 'hi' ]
  • Deze code laat een voorbeeld zien waarin elke string in een array wordt gesplitst op spaties, en de resultaten worden samengevoegd en vlak gemaakt tot één enkele array.

join

De join-methode maakt een string door de elementen van een array samen te voegen met een opgegeven scheidingsteken.

1// join: combine elements into a string with a separator
2const csv = ['a', 'b', 'c'].join(',');
3console.log("['a', 'b', 'c'].join(','):", csv);  // a,b,c
  • In deze code wordt de join-methode gebruikt om een array naar een door komma's gescheiden string om te zetten.

Veelvoorkomende valkuilen

Array-bewerkingen lijken op het eerste gezicht eenvoudig, maar er zijn meerdere punten die makkelijk tot ongewenst gedrag leiden. Veel valkuilen zijn makkelijk te missen in dagelijks gebruik; als je op de volgende punten let, wordt je code veel betrouwbaarder.

  • De sort() van Array sorteert standaard op stringvergelijking. Bij het correct sorteren van getallen moet je altijd een vergelijkingsfunctie opgeven.
  • Arrays kopiëren (via slice of spreidsyntax, etc.) maakt een oppervlakkige kopie (shallow copy). Als je arrays objecten bevatten, wees dan voorzichtig omdat de originele data onbedoeld kan worden aangepast.
  • splice is een destructieve methode die het array direct wijzigt, terwijl slice een niet-destructieve methode is die het originele array niet aanpast. Het is belangrijk ze correct te kiezen voor je behoeften.
  • forEach is niet geschikt voor loops met asynchrone verwerking met await. Als je betrouwbaar asynchrone code op volgorde wilt uitvoeren, gebruik dan for...of.

Praktisch voorbeeld

Hieronder zie je een voorbeeld waarbij array-methoden gecombineerd worden om 'de totale leeftijd uit gebruikersdata te halen, iedereen van 30 jaar of ouder te kiezen en van hen een namenlijst te maken.'.

 1const users = [
 2  { name: 'Alice', age: 28 },
 3  { name: 'Bob', age: 34 },
 4  { name: 'Carol', age: 41 },
 5  { name: 'Dave', age: 19 }
 6];
 7
 8console.log("Users:", users);
 9
10// sum ages
11const totalAge = users.reduce((acc, u) => acc + u.age, 0);
12console.log("\nTotal age of all users:", totalAge);
13// Total age of all users: 122
14
15// filter and map names of users 30 and older
16const namesOver30 = users
17  .filter(u => u.age >= 30)
18  .map(u => u.name);
19
20console.log("Users aged 30 or older (names):", namesOver30);
21// Users aged 30 or older (names): [ 'Bob', 'Carol' ]
  • Door reduce, filter en map te combineren, kun je aggregatie, conditie-extractie en transformatie van data eenvoudig en overzichtelijk schrijven.
  • Zo'n 'data processing pipeline' is zeer leesbaar en door het geringe aantal bijwerkingen wordt dit veel gebruikt in echte applicaties.

Samenvatting

Met JavaScript-arrays zijn zelfs basishandelingen breed toepasbaar, en door gebruik van hogere-orde-functies wordt je code beknopter en expressiever. Er zijn veel zaken om onder de knie te krijgen, maar als je weet hoe je alles juist toepast, gaat gegevensverwerking veel soepeler.

Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.

YouTube Video