`Array`-objekt
Denne artikel forklarer om Array-objektet.
Jeg vil forklare praktiske anvendelser af arrays trin for trin på en letforståelig måde.
YouTube Video
Array-objekt
JavaScripts Array-objekt er en af de vigtigste strukturer, der danner grundlaget for al slags databehandling. Fra grundlæggende array-operationer til højere ordens funktioner, der er nyttige for effektiv datatransformation, er der mange funktioner, du bør kende.
Grundlæggende om arrays
I JavaScript er arrays en grundlæggende datastruktur til håndtering af flere værdier sammen. Her introducerer vi, hvordan du opretter arrays, samt hvordan du læser og skriver deres elementer med enkle eksempler.
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
- Denne kode viser tre måder at oprette arrays på, hvordan man læser og opdaterer elementer ved hjælp af indekser, og hvordan man får længden ved hjælp af
length-egenskaben. - Array-litteraler er de mest almindelige og læselige, og bruges oftest i dagligdags situationer.
Tilføjelse og fjernelse af elementer (i slutningen eller starten)
Arrays gør det nemt at tilføje eller fjerne elementer i slutningen eller starten. Disse operationer er også nyttige, når man implementerer strukturer som stabler eller køer.
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' ]
pushogpopopererer i slutningen af et array. De er ideelle til implementering af stak-strukturer.unshiftogshiftopererer i begyndelsen af et array. Vær dog opmærksom på, at operationer i starten kræver, at alle elementers indekser forskydes internt, hvilket gør det til en dyr operation.
Håndtering af elementer i midten (splice og slice)
Når du håndterer elementer i midten af et array, skal du vælge mellem splice og slice afhængigt af, om du vil ændre det originale array eller ej. Hvis du blot vil udtrække en del af et array, skal du bruge slice; hvis du vil ændre selve arrayet, f.eks. ved at indsætte eller slette elementer, skal du bruge 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 ]
sliceudtrækker kun elementer og ændrer ikke det originale array.splicetilføjer eller fjerner elementer og ændrer selve array'et, så vær især opmærksom på dens indflydelse på adfærden.
Iteration (for / for...of / forEach)
Der er flere måder at bearbejde arrays sekventielt på, og du kan vælge efter formål og kodestilpræference. Her er tre typiske loop-konstruktioner.
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});for-løkken er den mest fleksible, da den tillader indeksoperationer og fin kontrol over iterationen medbreakog andre udsagn.for...ofgiver en kortfattet måde at håndtere elementværdier på og er mest balanceret med hensyn til læsbarhed.forEachgør det muligt at skrive kode i funktionel stil og er velegnet til operationer med bivirkninger, som logning eller opdatering af data for hvert element. Bemærk dog, at du ikke kan brugebreakellercontinue, og at det ikke er egnet til asynkron behandling medawait.
map / filter / reduce — Højere ordens funktioner
map, filter og reduce er højereordensfunktioner, der ofte bruges ved transformation, filtrering eller aggregering af arrays. Da du tydeligt kan udtrykke gentagne processer, bliver din kode enkel og let at forstå.
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
- Disse metoder gør det muligt at fokusere på, hvad du vil gøre på en deklarativ måde, hvilket forbedrer læsbarheden og mindsker uønskede bivirkninger.
find / findIndex / some / every
Her er et overblik over søge- og betingelsestjek-metoder. Disse er nyttige til at finde elementer, der opfylder bestemte betingelser, eller til at udføre booleske tjek på sættet.
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
findreturnerer det første element, der opfylder betingelsen.findIndexreturnerer indekset for det element, der opfylder betingelsen.somereturnerertrue, hvis der er mindst ét element, der opfylder betingelsen.everyreturnerertrue, hvis alle elementer opfylder betingelsen.
Disse metoder er meget nyttige i array-behandling, så brug dem passende for situationen for at holde koden kort og klar.
Sortering og sammenligningsfunktioner
Arrays sorteres med sort, men som standard sammenlignes elementer som strenge, hvilket kan føre til uventede resultater ved sortering af tal.
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 } ]
- Når du sorterer tal eller objekter, skal du altid angive en sammenligningsfunktion for at sortere dem i den ønskede rækkefølge.
- I en sammenligningsfunktion placerer en negativ returværdi
aførb, en positiv placererbføra, og 0 bevarer deres rækkefølge.
Array-kopiering og uforanderlighed
Når du kopierer arrays, er det vigtigt at forstå forskellen mellem 'referencekopi' og 'shallow copy' (overfladisk kopi). Vær især opmærksom på, at hvis der er objekter inde i array'et, vil en flad kopi få de indre objekter til at blive delt.
Referencekopi
Når du tildeler et array til en anden variabel, bliver selve arrayet ikke duplikeret; i stedet kopieres referencen, der peger på det samme array.
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 ]
- Ved en referencekopi vil ændringer i arrayet via den kopierede variabel også blive afspejlet i den oprindelige variabel, der refererer til det samme array.
Overfladisk kopi (shallow copy)
Ved at bruge slice() eller spread-syntaksen oprettes en 'overfladisk kopi', fordi kun værdierne duplikeres; det oprindelige og det kopierede array behandles som separate enheder.
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 ]
- Denne kode demonstrerer, at en overfladisk kopi af et array, oprettet med
slice()eller spread-syntaksen, ikke påvirker det oprindelige array.
Overfladisk kopi og uforanderlighed
Selv hvis du duplikerer et array med en 'overfladisk kopi', kan der opstå utilsigtet deling, hvis arrayet indeholder objekter.
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 } ]
- Denne kode viser, at ved overfladisk kopi deles de indre objekter, så ændringer på disse påvirker både det oprindelige array og kopien.
- Hvis du har brug for uafhængige data, skal du lave en 'dyb kopi' såsom ved at bruge
structuredClone()eller JSON-konvertering.
Nyttige hjælpefunktioner
Følgende er hjælpefunktioner, der ofte bruges, når man arbejder med arrays. Ved at bruge dem hensigtsmæssigt til dit formål kan du skrive kort og læsbar kode.
includes
includes-metoden kontrollerer, om en bestemt værdi findes i et 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
- I denne kode bruges
includes-metoden til let at afgøre, om en bestemt værdi findes i arrayet.
concat
concat-metoden returnerer et nyt array, hvor det angivne array eller værdier tilføjes til slutningen, mens det oprindelige array forbliver uændret.
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 ]
- Denne kode viser, at
concatikke er destruktiv, så du kan generere et nyt array, mens det oprindelige bevares.
flat
Med flat-metoden kan du udflade sammenkædede (næstede) arrays.
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 ] ]
- Denne kode demonstrerer resultatet af at udflade et array med ét niveau.
flatgør det muligt at angive dybden, så du fleksibelt kan udflade arrays efter behov.
flatMap
flatMap-metoden anvender en transformation på hvert element og udflader derefter automatisk resultaterne til et endimensionelt 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' ]
- Denne kode viser et eksempel, hvor hver streng i et array opdeles ved mellemrum, og resultaterne samles og udflades til ét enkelt array.
join
join-metoden opretter en streng ved at kombinere elementerne i et array med en angivet separator.
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
- I denne kode bruges
join-metoden til at konvertere et array til en kommasepareret streng.
Almindelige faldgruber
Array-operationer kan virke enkle ved første øjekast, men der er flere ting, som let kan føre til utilsigtet adfærd. Mange faldgruber er lette at overse under daglige array-operationer, så hvis du husker på følgende punkter, vil din kodes pålidelighed blive betydeligt forbedret.
Array'ssort()sorterer som standard ved strengsammenligning. Når du sorterer tal korrekt, skal du altid angive en sammenligningsfunktion.- Kopiering af arrays (via
sliceeller spread-syntaks m.m.) opretter en flad kopi. Hvis dine arrays indeholder objekter, så vær forsigtig, fordi de oprindelige data kan blive ændret utilsigtet. spliceer en destruktiv metode, der ændrer array'et direkte, menssliceer en ikke-destruktiv metode, der ikke ændrer det oprindelige array. Det er vigtigt at bruge dem rigtigt efter dine behov.forEacher ikke egnet til løkker med asynkron behandling ved brug afawait. Hvis du vil udføre asynkrone operationer i rækkefølge pålideligt, anbefales det at brugefor...of.
Praktisk eksempel
Nedenfor er et eksempel, der kombinerer array-metoder for at 'få den samlede alder fra brugerdata, udtrække dem der er 30 år eller ældre og lave en liste over navne.'.
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' ]
- Ved at kæde
reduce,filterogmapsammen kan du nemt skrive aggregering, betinget udtrækning og transformation af data. - En sådan 'databehandlingspipeline' er meget læsbar og bruges ofte i virkelige applikationer, da det er en stil med få bivirkninger.
Sammendrag
Med JavaScript-arrays er selv grundlæggende operationer meget alsidige, og ved at bruge højere ordens funktioner bliver din kode mere kortfattet og udtryksfuld. Der er mange ting at forstå, men når du først mestrer at bruge hver af dem korrekt, vil databehandlingen blive meget lettere.
Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.