12. februar 2026

Array-metoder

Array giver os mange metoder. For at gøre det nemmere, er de i dette kapitel opdelt i grupper.

Tilføj/fjern elementer

Vi kender allerede metoder, der tilføjer og fjerner elementer fra begyndelsen eller slutningen:

  • arr.push(...items) – tilføjer elementer til slutningen,
  • arr.pop() – fjerner et element fra slutningen,
  • arr.shift() – fjerner et element fra begyndelsen,
  • arr.unshift(...items) – tilføjer elementer til begyndelsen.

Her er et par andre.

splice

Hvordan sletter man et element fra arrayet?

Array er objekter, så vi kan prøve at bruge delete:

let arr = ["Jeg", "går", "hjem"];

delete arr[1]; // fjern "går"

alert( arr[1] ); // undefined

// nu er  arr = ["Jeg",  , "hjem"];
alert( arr.length ); // 3

Elementet blev slettet, men har stadig 3 elementer. Vi kan se, at arr.length == 3.

Det er naturligt, fordi delete obj.key fjerner en værdi ved key. Det er alt, hvad det gør. Fint for objekter. Men for arrays ønsker vi normalt, at resten af elementerne skal skifte og optage det frigjorte sted. Vi forventer at have et kortere array nu.

Derfor skal der bruges specielle metoder.

Metoden arr.splice er en schweizerkniv for arrays. Den kan det hele: indsætte, fjerne og erstatte elementer.

Syntaksen er:

arr.splice(start[, deleteCount, elem1, ..., elemN])

Den ændrer arr startende fra indekset start: fjerner deleteCount elementer og indsætter derefter elem1, ..., elemN på deres plads. Returnerer arrayet af fjernede elementer.

Metoden er nok nemmere at forstå ved hjælp af eksempler.

Lad os starte med sletning:

let arr = ["Jeg", "studerer", "JavaScript"];

arr.splice(1, 1); // fra indeks 1 fjern 1 element

alert( arr ); // ["Jeg", "JavaScript"]

Nem, ikke? Fra indeks 1 fjernede den 1 element.

I det næste eksempel fjerner vi 3 elementer og erstatter dem med de to andre:

let arr = ["Jeg", "studerer", "JavaScript", "lige", "nu"];

// fjerner de 3 første elementer og erstatter dem med andre
arr.splice(0, 3, "Lad", "os", "danse");

alert( arr ) // nu ["Lad", "os", "danse", "lige", "nu"]

Her kan vi se, at splice returnerer arrayet af fjernede elementer:

let arr = ["Jeg", "studerer", "JavaScript", "lige", "nu"];

// fjern de 2 første elementer
let removed = arr.splice(0, 2);

alert( removed ); // "Jeg", "studerer" <-- array af fjernede elementer

Metoden splice kan også indsætte elementer uden at fjerne nogen. For det skal vi sætte deleteCount til 0:

let arr = ["Jeg", "studerer", "JavaScript"];

// fra indeks 2
// slet 0
// indsæt "et", "kompleks" og "sprog"
arr.splice(2, 0, "et", "kompleks", "sprog");

alert( arr ); // "Jeg", "studerer", "kompleks", "sprog", "JavaScript"
Negative indeks er tilladt

Her og i andre array-metoder er negative indekser tilladt. De angiver positionen fra slutningen af arrayet, som her:

let arr = [1, 2, 5];

// fra indeks -1 (et skridt fra slutningen)
// slet 0 elementer,
// indsæt derefter 3 og 4
arr.splice(-1, 0, 3, 4);

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

slice

Metoden arr.slice er meget enklere end den lignende arr.splice.

Syntaksen er:

arr.slice([start], [end])

Den returnerer et nyt array, der kopierer alle elementer fra indeks start til end (eksklusiv end). Både start og end kan være negative, i så fald antages positionen fra slutningen af arrayet.

Det ligner string-metoden str.slice, men i stedet for substrings laver den subarrays.

For eksempel:

let arr = ["t", "e", "s", "t"];

alert( arr.slice(1, 3) ); // e,s (kopier fra 1 til 3)

alert( arr.slice(-2) ); // s,t (kopier fra -2 til slutningen)

Vi kan også kalde den uden argumenter: arr.slice() laver en kopi af arr. Det bruges ofte til at få en kopi til videre transformationer, der ikke skal påvirke det oprindelige array.

concat

Metoden arr.concat opretter et nyt array der inkluderer værdierne fra andre arrays og eventuelle yderligere elementer.

Syntaksen er:

arr.concat(arg1, arg2...)

Den accepterer et vilkårligt antal argumenter – enten arrays eller værdier.

Resultatet er et nyt array, der indeholder elementer fra arr, derefter arg1, arg2 osv.

Hvis et argument argN er et array, kopieres alle dets elementer. Ellers kopieres argumentet selv.

For eksempel:

let arr = [1, 2];

// Opret et array fra: arr og [3,4]
alert( arr.concat([3, 4]) ); // 1,2,3,4

// Opret et array fra: arr og [3,4] og [5,6]
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

// Opret et array fra: arr og [3,4], og tilføj derefter værdierne 5 og 6
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6

Normalt kopierer den kun elementer fra arrays. Andre objekter, selvom de ligner arrays, tilføjes som helhed:

let arr = [1, 2];

let arrayLike = {
  0: "noget",
  length: 1
};

alert( arr.concat(arrayLike) ); // 1,2,[object Object]

…Men hvis et array-lignende objekt har en speciel Symbol.isConcatSpreadable egenskab, behandles det som et array af concat: dets elementer tilføjes i stedet:

let arr = [1, 2];

let arrayLike = {
  0: "noget",
  1: "andet",
  [Symbol.isConcatSpreadable]: true,
  length: 2
};

alert( arr.concat(arrayLike) ); // 1,2,noget,andet

Iteration: forEach

Metoden arr.forEach tillader at køre en funktion for hvert element i arrayet.

Syntaksen er:

arr.forEach(function(item, index, array) {
  // ... gør noget med elementet
});

For eksempel, dette viser hvert element i arrayet:

// for hvert element kald alert
["Bilbo", "Gandalf", "Nazgul"].forEach(alert);

Og denne kode er mere detaljeret om deres positioner i mål-arrayet:

["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
  alert(`${item} er ved indeks ${index} i ${array}`);
});

Resultatet af funktionen (hvis den returnerer noget) kasseres og ignoreres.

Søgning i array

Lad os nu dække metoder, der søger i et array.

indexOf/lastIndexOf og includes

Metoderne arr.indexOf og arr.includes har en lignende syntaks og gør stort set det samme som deres string-modstykker, men opererer på elementer i stedet for tegn:

  • arr.indexOf(item, from) – leder efter item startende fra indeks from, og returnerer indekset hvor det blev fundet, ellers -1.
  • arr.includes(item, from) – leder efter item startende fra indeks from, returnerer true hvis fundet.

Normalt bruges disse metoder med kun ét argument: det item der skal søges efter. Som standard er søgningen fra starten.

For eksempel:

let arr = [1, 0, false];

alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1

alert( arr.includes(1) ); // true

Bemærk, at indexOf bruger streng lighed === til sammenligning. Så hvis vi leder efter false, finder den præcis false og ikke nul.

Hvis vi vil tjekke, om item findes i arrayet og ikke har brug for indekset, er arr.includes at foretrække.

Metoden arr.lastIndexOf er den samme som indexOf, men søger fra højre mod venstre.

let fruits = ['Æble', 'Appelsin', 'Æble']

alert( fruits.indexOf('Æble') ); // 0 (første Æble)
alert( fruits.lastIndexOf('Æble') ); // 2 (sidste Æble)
Metoden includes håndterer NaN korrekt

En lille, men bemærkelsesværdig funktion ved includes er, at den korrekt håndterer NaN, i modsætning til indexOf:

const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (forkert, burde være 0)
alert( arr.includes(NaN) );// true (korrekt)

Dette er fordi includes blev tilføjet til JavaScript meget senere og bruger den mere opdaterede sammenligningsalgoritme internt.

find og findIndex/findLastIndex

Forestil dig, at vi har et array af objekter. Hvordan finder vi et objekt med en bestemt betingelse?

Her kommer metoden arr.find(fn) til hjælp.

Syntaksen er:

let result = arr.find(function(item, index, array) {
  // hvis true returneres, returneres item og iterationen stoppes
  // for falsy scenarier returneres undefined
});

Funktionen kaldes for elementer i arrayet, et efter et:

  • item er elementet.
  • index er dets indeks.
  • array er selve arrayet.

Hvis det returnerer true, stoppes søgningen, og item returneres. Hvis intet findes, returneres undefined.

For eksempel, vi har et array af brugere, hver med felterne id og name. Lad os finde den med id == 1:

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

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

alert(user.name); // John

I virkelige projekter er arrays af objekter en almindelig ting, så find-metoden er meget nyttig.

Bemærk, at i eksemplet giver vi kun ét argument til find funktionen item => item.id == 1. Det er typisk, andre argumenter til denne funktion bruges sjældent.

Metoden arr.findIndex har samme syntaks, men returnerer indekset hvor elementet blev fundet i stedet for elementet selv. Værdien -1 returneres hvis intet findes.

Metoden arr.findLastIndex er som findIndex, men søger fra højre mod venstre, ligesom lastIndexOf.

Her er et eksempel:

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

// Find indeks med den første John
alert(users.findIndex(user => user.name == 'John')); // 0

// Find indeks med den sidste John
alert(users.findLastIndex(user => user.name == 'John')); // 3

filter

Metoden find søger efter et enkelt (første) element der får funktionen til at returnere true.

Hvis der kan være mange, kan vi bruge arr.filter(fn).

Syntaksen er lignende find, men filter returnerer et array af alle matchende elementer:

let results = arr.filter(function(item, index, array) {
  // hvis true bliver item tilføjet til results, og iterationen fortsætter
  // returnerer et tomt array hvis intet findes
});

For eksempel:

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

// returnerer et array med de første to brugere
let someUsers = users.filter(item => item.id < 3);

alert(someUsers.length); // 2

Transformér et array

Lad os gå videre til metoder, der transformerer og omarrangerer et array.

map

Metoden arr.map er en af de mest nyttige og ofte brugte.

Den kalder funktionen for hvert element i arrayet og returnerer arrayet af resultater.

Syntaksen er:

let result = arr.map(function(item, index, array) {
  // returner den nye værdi i stedet for item
});

For eksempel, her transformerer vi hvert element til dets længde:

let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6

sort(fn)

Kaldet til arr.sort() sorterer arrayet i sig selv, og ændrer dermed rækkefølgen af dets egne elementer.

Det returnerer også det sorterede array, men den returnerede værdi ignoreres normalt, da arr selv bliver ændret.

For eksempel:

let arr = [ 1, 2, 15 ];

// metoden omarrangerer indholdet af arr
arr.sort();

alert( arr );  // 1, 15, 2

Bemærkede du noget mærkeligt i resultatet?

Rækkefølgen blev 1, 15, 2. Forkert. Men hvorfor?

Elementerne sorteres som standard som tekststrenge (string).

Det betyder at alle elementer konverteres til strenge for sammenligninger. For strenge anvendes leksikografisk orden, og faktisk er "2" > "15".

For at bruge vores egen sorteringsorden skal vi levere en funktion som argument til arr.sort().

Funktionen skal sammenligne to vilkårlige værdier og returnere:

function compare(a, b) {
  if (a > b) return 1; // hvis den første værdi er større end den anden
  if (a == b) return 0; // hvis værdierne er lige
  if (a < b) return -1; // hvis den første værdi er mindre end den anden
}

For eksempel kan vi sortere som tal således:

function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}

let arr = [ 1, 2, 15 ];

arr.sort(compareNumeric);

alert(arr);  // 1, 2, 15

Nu virker det som forventet.

Lad os træde et skridt tilbage og tænke over, hvad der sker. arr kan være et array af hvad som helst, ikke? Det kan indeholde tal eller strenge eller objekter eller hvad som helst. Vi har et sæt af nogle elementer. For at sortere det, har vi brug for en ordensfunktion, der ved, hvordan man sammenligner dets elementer. Standard er en strengorden.

Metoden arr.sort(fn) implementerer en generisk sorteringsalgoritme. Vi behøver ikke bekymre os om, hvordan den internt fungerer (en optimeret quicksort eller Timsort det meste af tiden). Den vil gå igennem arrayet, sammenligne dets elementer ved hjælp af den angivne funktion og omarrangere dem, alt hvad vi behøver er at levere fn, som gør sammenligningen.

Forresten, hvis vi nogensinde vil vide, hvilke elementer der sammenlignes – intet forhindrer os i at vise dem med alert:

[1, -2, 15, 2, 0, 8].sort(function(a, b) {
  alert( a + " <> " + b );
  return a - b;
});

Algoritmen kan sammenligne et element med flere andre i processen, men den prøver at lave så få sammenligninger som muligt.

En sammenligningsfunktion kan returnere et hvilket som helst tal

Faktisk kræves det kun, at en sammenligningsfunktion returnerer et positivt tal for at angive “større” og et negativt tal for at angive “mindre”.

Det tillader os at skrive kortere funktioner:

let arr = [ 1, 2, 15 ];

arr.sort(function(a, b) { return a - b; });

alert(arr);  // 1, 2, 15
Arrow funktioner for de øvede

Husk arrow functions? Vi kan bruge dem her for en pænere sortering:

arr.sort( (a, b) => a - b );

Det virker præcis på samme måde som den længere version ovenfor.

Brug localeCompare til strenge

Husk du strings sammenligningsalgoritmen? Den sammenligner bogstaver efter deres koder som standard.

For mange alfabeter er det bedre at bruge metoden str.localeCompare for korrekt at sortere bogstaver, såsom Ö og “Æ”, “Ø” og “Å”.

For eksempel, lad os sortere nogle lande på tysk:

let countries = ['Österreich', 'Andorra', 'Vietnam'];

alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (forkert)

alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (korrekt!)

reverse

Metoden arr.reverse vender rækkefølgen af elementerne i arr.

For eksempel:

let arr = [1, 2, 3, 4, 5];
arr.reverse();

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

Den returnerer også arrayet arr efter omvendelsen.

split og join

Her er situationen fra det virkelige liv. Vi skriver en beskedapp, og personen indtaster en kommasepareret liste over modtagere: John, Pete, Mary. Men for os ville et array af navne være meget mere bekvemt end en enkelt streng. Hvordan får vi det?

Metoden str.split(delim) gør præcis det. Den splitter strengen op i et array efter den givne delimiter delim.

I eksempel nedenfor splitter vi efter et komma efterfulgt af et mellemrum:

let names = 'Bilbo, Gandalf, Nazgul';

let arr = names.split(', ');

for (let name of arr) {
  alert( `En besked til ${name}.` ); // En besked til Bilbo  (efterfulgt af de andre navne)
}

Metoden split har et valgfrit andet numerisk argument – en grænse for arrayets længde. Hvis det er angivet, ignoreres de ekstra elementer. I praksis bruges det dog sjældent:

let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);

alert(arr); // Bilbo, Gandalf
Opsplitning i bogstaver

Kaldet til split(s) med en tom s vil opdele strengen i et array af bogstaver:

let str = "test";

alert( str.split('') ); // t,e,s,t

Et kald til arr.join(glue) gør det modsatte af split. Det skaber en streng af arr elementer forbundet med glue imellem dem.

For eksempel:

let arr = ['Bilbo', 'Gandalf', 'Nazgul'];

let str = arr.join(';'); // limer arrayet sammen til en streng ved hjælp af ;

alert( str ); // Bilbo;Gandalf;Nazgul

reduce/reduceRight

Når vi har brug for at iterere over et array – kan vi bruge forEach, for eller for..of.

Når vi har brug for at iterere og returnere data for hvert element – kan vi bruge map.

Metoderne arr.reduce og arr.reduceRight hører også til den kategori, men er lidt mere indviklede. De bruges til at beregne en enkelt værdi baseret på arrayet.

Syntaksen er:

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

Funktionen anvendes på alle array-elementer én efter én og “bærer” sit resultat videre til det næste kald.

Argumenterne for funktionen er:

  • accumulator – er resultatet af det forrige funktionskald, svarer til initial første gang (hvis initial er angivet).
  • item – er det aktuelle array-element.
  • index – er dets position.
  • array – er arrayet.

Efterhånden som funktionen anvendes, bliver resultatet af det forrige funktionskald videregivet til det næste som det første argument.

Så det første argument er i bund og grund akkumulatoren, der gemmer det kombinerede resultat af alle tidligere udførelser. Og til sidst bliver det resultatet af reduce.

Lyder det kompliceret?

Den nemmeste måde at forstå det på er ved eksempel.

Her får vi summen af et array på én linje:

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

let result = arr.reduce((sum, current) => sum + current, 0);

alert(result); // 15

Funktionen, der gives til reduce, bruger kun 2 argumenter, det er typisk nok.

Lad os se detaljerne i, hvad der sker.

  1. Ved første kørsel er sum den initial værdi (det sidste argument til reduce), som er 0, og current er det første array-element, som er 1. Så funktionens resultat er 1.
  2. Ved anden kørsel er sum = 1, vi lægger det andet array-element (2) til det og returnerer.
  3. Ved tredje kørsel er sum = 3 og vi lægger endnu et element til det, og så videre…

Beregningen forløber således:

Eller opsat i form af en tabel, hvor hver række repræsenterer et funktionskald på det næste array-element:

sum current result
det før første kald 0 1 1
det andet kald 1 2 3
det tredje kald 3 3 6
det fjerde kald 6 4 10
det femte kald 10 5 15

Her kan vi tydeligt se, hvordan resultatet af det forrige kald bliver det første argument i det næste.

Vi kan også udelade den initiale værdi, så reduce starter med det første element i arrayet som det første argument og starter iterationen fra det andet element:

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

// fjernet initial værdi fra reduce (ingen 0 til sidst)
let result = arr.reduce((sum, current) => sum + current);

alert( result ); // 15

Resultatet er det samme. Det sker fordi reduce tager det første element i arrayet som den initiale værdi og starter iterationen fra det andet element hvis der ikke er angivet en initial værdi.

Beregningstabellen er den samme som ovenfor, minus den første række.

Men sådan brug kræver at du er lidt opmærksom. Hvis arrayet er tomt, så giver et kald til reduce uden initial værdi en fejl.

Her er et eksempel:

let arr = [];

// TypeError: Reduce of empty array with no initial value
// hvis den initiale værdi eksisterede, ville reduce returnere den for det tomme array.
arr.reduce((sum, current) => sum + current);

Så det anbefales altid at angive den initiale værdi.

Metoden arr.reduceRight gør det samme, men går fra højre mod venstre.

Array.isArray

Et array er ikke en separat datatype. De er baseret på objekter.

typeof hjælper ikke med at skelne den fra et regulært objekt:

alert(typeof {}); // object
alert(typeof []); // object (samme)

…Men arrays bruges så ofte, at der findes en speciel metode til det: Array.isArray(value). Den returnerer true, hvis value er et array, og false ellers.

alert(Array.isArray({})); // false

alert(Array.isArray([])); // true

De fleste metoder understøtter “thisArg”

Næsten alle array-metoder der kalder funktioner – som find, filter, map, med en bemærkelsesværdig undtagelse af sort, accepterer en valgfri ekstra parameter thisArg.

Den parameter er ikke forklaret i afsnittene ovenfor, fordi den sjældent bruges. Men for fuldstændighedens skyld skal vi dække den.

Her er den fulde syntaks for disse metoder:

arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg er det valgfrie sidste argument

Værdien af thisArg-parameteren bliver this for func.

For eksempel, her bruger vi en metode af army-objektet som et filter, og thisArg sender konteksten til det:

let army = {
  minAge: 18,
  maxAge: 27,
  canJoin(user) {
    return user.age >= this.minAge && user.age < this.maxAge;
  }
};

let users = [
  {age: 16},
  {age: 20},
  {age: 23},
  {age: 30}
];

// find users, for hvem army.canJoin returnerer true
let soldiers = users.filter(army.canJoin, army);

alert(soldiers.length); // 2
alert(soldiers[0].age); // 20
alert(soldiers[1].age); // 23

Hvis vi, i eksemplet ovenfor, brugte users.filter(army.canJoin), ville army.canJoin blive kaldt som en selvstændig funktion, med this=undefined, hvilket ville føre til en øjeblikkelig fejl.

Et kald til users.filter(army.canJoin, army) kan erstattes med users.filter(user => army.canJoin(user)), som gør det samme. Den sidstnævnte bruges oftere, da den er lidt nemmere at forstå for de fleste.

Opsummering

Et cheat sheet over array-metoder:

  • For at tilføje/fjerne elementer:

    • push(...items) – tilføjer elementer til slutningen,
    • pop() – fjerner et element fra slutningen,
    • shift() – fjerner et element fra begyndelsen,
    • unshift(...items) – tilføjer elementer til begyndelsen.
    • splice(pos, deleteCount, ...items) – ved indeks pos sletter deleteCount elementer og indsætter items.
    • slice(start, end) – opretter et nyt array, kopierer elementer fra indekset start til end (ikke inklusiv) ind i det.
    • concat(...items) – opretter et nyt array: kopierer alle elementer fra det nuværende og tilføjer items til det. Hvis nogen af items er et array, tages dets elementer.
  • For at søge blandt elementer:

    • indexOf/lastIndexOf(item, pos) – leder efter item startende fra position pos, og returnerer indekset eller -1 hvis ikke fundet.
    • includes(value) – returnerer true hvis arrayet har value, ellers false.
    • find/filter(func) – filtrerer elementer gennem funktionen, returnerer første/alle værdier der får den til at returnere true.
    • findIndex er som find, men returnerer indekset i stedet for en værdi.
  • For at iterere over elementer:

    • forEach(func) – kalder func for hvert element, returnerer ikke noget.
  • For at transformere arrayet:

    • map(func) – opretter et nyt array fra resultaterne af at kalde func for hvert element.
    • sort(func) – sorterer arrayet på stedet, og returnerer det.
    • reverse() – vender arrayet om på stedet, og returnerer det.
    • split/join – konverterer en streng til et array og tilbage.
    • reduce/reduceRight(func, initial) – beregner en enkelt værdi over arrayet ved at kalde func for hvert element og videregive et mellemliggende resultat mellem kald.
  • Derudover:

    • Array.isArray(value) tjekker om value er et array, hvis ja returnerer true, ellers false.

Bemærk venligst, at metoderne sort, reverse og splice ændrer arrayet selv.

Disse metoder er de mest brugte, de dækker 99% af brugstilfælde. Men der er få andre:

  • arr.some(fn)/arr.every(fn) tjekker arrayet.

    Funktionen fn kaldes på hvert element i arrayet på samme måde som med map. Hvis nogen/alle resultater er true, returneres true, ellers false.

    Disse metoder opfører sig lidt som || og && operatorerne: hvis fn returnerer en sand værdi, returnerer arr.some() straks true og stopper iterationen over de resterende elementer; hvis fn returnerer en falsk værdi, returnerer arr.every() straks false og stopper iterationen over de resterende elementer.

    Vi kan bruge every til at sammenligne arrays:

    function arraysEqual(arr1, arr2) {
      return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
    }
    
    alert( arraysEqual([1, 2], [1, 2])); // true
  • arr.fill(value, start, end) – fylder arrayet med value fra indeks start til end.

  • arr.copyWithin(target, start, end) – kopierer sine elementer fra position start til position end ind i sig selv, ved position target (overskriver eksisterende).

  • arr.flat(depth)/arr.flatMap(fn) opretter et nyt fladt array fra et multidimensionelt array.

For the komplette fulde liste, se manualen.

Ved første øjekast kan det virke som om der er så mange metoder, at det er ret svært at huske dem alle. Men ved at huske de vigtigste er du langt.

Kig på cheat sheet ovenfor og øv dig på dem. Løs derefter opgaverne i dette kapitel for at øve dig, så du får erfaring med array-metoder.

Herefter, når du har brug for at gøre noget med et array, og du ikke ved hvordan – kom herhen, kig på cheat sheet og find den rigtige metode. Eksempler vil hjælpe dig med at skrive det korrekt. Snart vil du automatisk huske metoderne, uden specifikke anstrengelser fra din side.

Opgaver

vigtighed: 5

Skriv funktionen camelize(str), der ændrer ord adskilt af bindestreger som “min-korte-streng” til camelCase “minKorteStreng”.

Det vil sige: fjern alle bindestreger, og hvert ord efter en bindestreg bliver skrevet med stort begyndelsesbogstav.

Eksempler:

camelize("background-color") == 'backgroundColor';
camelize("list-style-image") == 'listStyleImage';
camelize("-webkit-transition") == 'WebkitTransition';

Hint: brug split til at opdele strengen i et array, transformér det og brug join til at sætte det sammen igen.

Åbn en sandbox med tests.

function camelize(str) {
  return str
    .split('-') // splitter 'mit-lange-ord' til array ['mit', 'lange', 'ord']
    .map(
      // gør første bogstav stort i alle array-elementer undtagen det første
      // konverterer ['mit', 'lange', 'ord'] til ['mit', 'Lange', 'Ord']
      (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)
    )
    .join(''); // samler ['mit', 'Lange', 'Ord'] til 'mitLangeOrd'
}

Åbn løsningen med tests i en sandbox.

vigtighed: 4

Skriv en funktion filterRange(arr, a, b) der modtager et array arr, leder efter elementer med værdier højere eller lig med a og lavere eller lig med b og returnerer resultatet som et array.

Funktionen må ikke ændre arrayet. Den skal returnere det nye array.

For eksempel:

let arr = [5, 3, 8, 1];

let filtered = filterRange(arr, 1, 4);

alert( filtered ); // 3,1 (matchende værdier)

alert( arr ); // 5,3,8,1 (ikke ændret)

Åbn en sandbox med tests.

function filterRange(arr, a, b) {
  // tilføjer krøllede parenteser omkring udtrykket for bedre læsbarhed
  return arr.filter(item => (a <= item && item <= b));
}

let arr = [5, 3, 8, 1];

let filtered = filterRange(arr, 1, 4);

alert( filtered ); // 3,1 (matchende værdier)

alert( arr ); // 5,3,8,1 (ikke ændret)

Åbn løsningen med tests i en sandbox.

vigtighed: 4

Skriv en funktion filterRangeInPlace(arr, a, b) der modtager et array arr og fjerner fra det alle værdier undtagen dem, der ligger mellem a og b. Testen er: a ≤ arr[i] ≤ b.

Funktionen skal kun ændre arrayet. Den må ikke returnere noget.

For instance:

let arr = [5, 3, 8, 1];

filterRangeInPlace(arr, 1, 4); // fjernede alle tal undtaget dem fra 1 til 4

alert( arr ); // [3, 1]

Åbn en sandbox med tests.

function filterRangeInPlace(arr, a, b) {

  for (let i = 0; i < arr.length; i++) {
    let val = arr[i];

    // fjern hvis uden for intervallet
    if (val < a || val > b) {
      arr.splice(i, 1);
      i--;
    }
  }

}

let arr = [5, 3, 8, 1];

filterRangeInPlace(arr, 1, 4); // fjernede alle tal undtaget dem fra 1 til 4

alert( arr ); // [3, 1]

Åbn løsningen med tests i en sandbox.

vigtighed: 4
let arr = [5, 2, 1, -10, 8];

// ... din kode for at sortere i faldende rækkefølge

alert( arr ); // 8, 5, 2, 1, -10
let arr = [5, 2, 1, -10, 8];

arr.sort((a, b) => b - a);

alert( arr );
vigtighed: 5

Vi har et array af strenge arr. Vi vil gerne have en sorteret kopi af det, men beholde arr uændret.

Lav en funktion copySorted(arr), der returnerer en sådan kopi.

let arr = ["HTML", "JavaScript", "CSS"];

let sorted = copySorted(arr);

alert( sorted ); // CSS, HTML, JavaScript
alert( arr ); // HTML, JavaScript, CSS (ingen ændring)

Vi kan bruge slice() til at lave en kopi og køre sorteringen på den:

function copySorted(arr) {
  return arr.slice().sort();
}

let arr = ["HTML", "JavaScript", "CSS"];

let sorted = copySorted(arr);

alert( sorted );
alert( arr );

P.S: Der findes en metode der hedder toSorted, som laver en sorteret kopi af et array uden at ændre det originale array. Den er en del af den nye ECMAScript-standard og er tilgængelig i moderne JavaScript-miljøer.

vigtighed: 5

Opret en konstruktørfunktion Calculator, der skaber “udvidelige” lommeregner-objekter.

Opgaven består af to dele.

  1. Først skal du implementere metoden calculate(str), der tager en streng som "1 + 2" i formatet “NUMMER operator NUMMER” (adskilt af mellemrum) og returnerer resultatet. Den skal forstå plus + og minus -.

    Brugseksempel:

    let calc = new Calculator;
    
    alert( calc.calculate("3 + 7") ); // 10
  2. Så skal du tilføje metoden addMethod(name, func), der lærer lommeregneren en ny operation. Den tager operatorens name og den to-argument funktion func(a,b), der implementerer den.

    For eksempel, lad os tilføje multiplikation *, division / og potens **:

    let powerCalc = new Calculator;
    powerCalc.addMethod("*", (a, b) => a * b);
    powerCalc.addMethod("/", (a, b) => a / b);
    powerCalc.addMethod("**", (a, b) => a ** b);
    
    let result = powerCalc.calculate("2 ** 3");
    alert( result ); // 8
  • Ingen parenteser eller komplekse udtryk i denne opgave.
  • Tallene og operatoren er adskilt med præcis ét mellemrum.
  • Der kan tilføjes fejlhåndtering, hvis du ønsker det.

Åbn en sandbox med tests.

  • Vær opmærksom på, hvordan metoderne gemmes. De tilføjes simpelthen til this.methods-egenskaben.
  • Alle tests og numeriske konverteringer udføres i calculate-metoden. I fremtiden kan den udvides til at understøtte mere komplekse udtryk.
function Calculator() {

  this.methods = {
    "-": (a, b) => a - b,
    "+": (a, b) => a + b
  };

  this.calculate = function (str) {

    let split = str.split(' '),
      a = +split[0],
      op = split[1],
      b = +split[2];

    if (!this.methods[op] || isNaN(a) || isNaN(b)) {
      return NaN;
    }

    return this.methods[op](a, b);
  };

  this.addMethod = function (name, func) {
    this.methods[name] = func;
  };
}

Åbn løsningen med tests i en sandbox.

vigtighed: 5

Du har et array af user objekter, hvor hvert objekt har en user.name. Skriv koden, der konverterer det til et array af navne.

For eksempel:

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };

let users = [ john, pete, mary ];

let names = /* ... din kode */;

alert( names ); // John, Pete, Mary
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };

let users = [ john, pete, mary ];

let names = users.map(item => item.name);

alert( names ); // John, Pete, Mary
vigtighed: 5

Du har et array af user objekter, hvert med name, surname og id.

Skriv koden, der laver et nyt array fra det, bestående af objekter med id og fullName, hvor fullName genereres ud fra name og surname.

For eksempel:

let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };

let users = [ john, pete, mary ];

let usersMapped = /* ... din kode ... */

/*
usersMapped = [
  { fullName: "John Smith", id: 1 },
  { fullName: "Pete Hunt", id: 2 },
  { fullName: "Mary Key", id: 3 }
]
*/

alert( usersMapped[0].id ) // 1
alert( usersMapped[0].fullName ) // John Smith
``` Så, faktisk skal du mappe et array af objekter til et andet. Prøv at bruge `=>` her. Der er en lille "catch".
let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };

let users = [ john, pete, mary ];

let usersMapped = users.map(user => ({
  fullName: `${user.name} ${user.surname}`,
  id: user.id
}));

/*
usersMapped = [
  { fullName: "John Smith", id: 1 },
  { fullName: "Pete Hunt", id: 2 },
  { fullName: "Mary Key", id: 3 }
]
*/

alert( usersMapped[0].id ); // 1
alert( usersMapped[0].fullName ); // John Smith

Vær opmærksom på at med arrow funktionen vi brug for yderligere parenteser.

:

let usersMapped = users.map(user => {
  fullName: `${user.name} ${user.surname}`,
  id: user.id
});

Som vi husker, er der to typer arrow funktioner: uden krop value => expr og med krop value => {...}.

I dette tilfælde vil JavaScript behandle { som starten på funktionskroppen, ikke starten på objektet. Løsningen er at omslutte dem i “almindelige” parenteser:

let usersMapped = users.map(user => ({
  fullName: `${user.name} ${user.surname}`,
  id: user.id
}));

Nu virker det fint.

vigtighed: 5

Skriv en funktion sortByAge(users), der modtager et array af objekter med egenskaben age og sorterer dem efter age.

For eksempel:

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };

let arr = [ pete, john, mary ];

sortByAge(arr);

// nu: [john, mary, pete]
alert(arr[0].name); // John
alert(arr[1].name); // Mary
alert(arr[2].name); // Pete
function sortByAge(arr) {
  arr.sort((a, b) => a.age - b.age);
}

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };

let arr = [ pete, john, mary ];

sortByAge(arr);

// nu er de sorteret: [john, mary, pete]
alert(arr[0].name); // John
alert(arr[1].name); // Mary
alert(arr[2].name); // Pete
vigtighed: 3

Skriv en funktion shuffle(array) der blander (tilfældigt omarrangerer) elementerne i arrayet.

Flere køringer af shuffle kan føre til forskellige rækkefølger af elementer. For eksempel:

let arr = [1, 2, 3];

shuffle(arr);
// arr = [3, 2, 1]

shuffle(arr);
// arr = [2, 1, 3]

shuffle(arr);
// arr = [3, 1, 2]
// ...

Alle elementrækkefølger skal have lige stor sandsynlighed. For eksempel kan [1,2,3] omarrangeres som [1,2,3] eller [1,3,2] eller [3,1,2] osv., med lige stor sandsynlighed for hver tilfældighed.

Den simple løsning kunne være:

function shuffle(array) {
  array.sort(() => Math.random() - 0.5);
}

let arr = [1, 2, 3];
shuffle(arr);
alert(arr);

Det virker nogenlunde, fordi Math.random() - 0.5 er et tilfældigt tal, der kan være positivt eller negativt, så sorteringsfunktionen omarrangerer elementerne tilfældigt.

Men fordi sorteringsfunktionen ikke er beregnet til at blive brugt på denne måde, har ikke alle permutationer samme sandsynlighed.

For eksempel, se på koden nedenfor. Den kører shuffle 1000000 gange og tæller forekomster af alle mulige resultater:

function shuffle(array) {
  array.sort(() => Math.random() - 0.5);
}

// tæller forekomster af alle mulige permutationer
let count = {
  '123': 0,
  '132': 0,
  '213': 0,
  '231': 0,
  '321': 0,
  '312': 0
};

for (let i = 0; i < 1000000; i++) {
  let array = [1, 2, 3];
  shuffle(array);
  count[array.join('')]++;
}

// vis tællinger af alle mulige permutationer
for (let key in count) {
  alert(`${key}: ${count[key]}`);
}

Et eksempel resultat (afhænger af JS-motoren):

123: 250706
132: 124425
213: 249618
231: 124880
312: 125148
321: 125223

Vi kan tydeligt se skævheden: 123 og 213 forekommer meget oftere end andre.

Resultatet af koden kan variere mellem JavaScript-motorer, men vi kan allerede se, at tilgangen er upålidelig.

Hvorfor virker det ikke? Generelt set er sort en “sort boks”: vi smider et array og en sammenligningsfunktion ind i den og forventer, at arrayet bliver sorteret. Men på grund af den totale tilfældighed i sammenligningen går den sorte boks amok, og hvordan den præcist går amok afhænger af den konkrete implementering, der varierer mellem motorer.

Der findes andre gode måder at løse opgaven på. For eksempel er der en fremragende algoritme kaldet Fisher-Yates shuffle. Ideen er at gå arrayet igennem i omvendt rækkefølge og bytte hvert element med et tilfældigt et før det. Det ser sådan ud:

function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i

    // bytter elementerne array[i] og array[j]
    // vi bruger "destructuring assignment" syntaks til at opnå det
    // du vil finde flere detaljer om den syntaks i senere kapitler
    // det samme kan skrives som:
    // let t = array[i]; array[i] = array[j]; array[j] = t
    [array[i], array[j]] = [array[j], array[i]];
  }
}

Lad os teste det på samme måde:

function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

// antal forekomster af alle mulige permutationer
let count = {
  '123': 0,
  '132': 0,
  '213': 0,
  '231': 0,
  '321': 0,
  '312': 0
};

for (let i = 0; i < 1000000; i++) {
  let array = [1, 2, 3];
  shuffle(array);
  count[array.join('')]++;
}

// vis tællinger af alle mulige permutationer
for (let key in count) {
  alert(`${key}: ${count[key]}`);
}

The example output:

123: 166693
132: 166647
213: 166628
231: 167517
312: 166199
321: 166316

Det ser bedre ud: alle permutationer forekommer med samme sandsynlighed.

Desuden er Fisher-Yates-algoritmen meget bedre med hensyn til ydeevne, da der ikke er nogen “sorterings” overhead.

vigtighed: 4

Skriv funktionen getAverageAge(users), der modtager et array af objekter med egenskaben age og returnerer gennemsnitsalderen.

Formlen for gennemsnittet er (age1 + age2 + ... + ageN) / N.

For eksempel:

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 };

let arr = [ john, pete, mary ];

alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28
function getAverageAge(users) {
  return users.reduce((prev, user) => prev + user.age, 0) / users.length;
}

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 };

let arr = [ john, pete, mary ];

alert( getAverageAge(arr) ); // 28
vigtighed: 4

Lad arr være et array.

Opret en funktion unique(arr), der skal returnere et array med unikke elementer fra arr.

For eksempel:

function unique(arr) {
  /* din kode */
}

let strings = ["Hare", "Krishna", "Hare", "Krishna",
  "Krishna", "Krishna", "Hare", "Hare", ":-O"
];

alert( unique(strings) ); // Hare, Krishna, :-O

Åbn en sandbox med tests.

Lad os gennemgå array-elementerne:

  • For hvert element tjekker vi, om det resulterende array allerede indeholder det element.
  • Hvis det er tilfældet, ignorerer vi det, ellers tilføjer vi det til resultatet.
function unique(arr) {
  let result = [];

  for (let str of arr) {
    if (!result.includes(str)) {
      result.push(str);
    }
  }

  return result;
}

let strings = ["Hare", "Krishna", "Hare", "Krishna",
  "Krishna", "Krishna", "Hare", "Hare", ":-O"
];

alert( unique(strings) ); // Hare, Krishna, :-O

Koden virker, men der er et potentielt ydelsesproblem i den.

Metoden result.includes(str) gennemgår internt arrayet result og sammenligner hvert element med str for at finde et match.

Så hvis der er 100 elementer i result og ingen matcher str, vil den gennemgå hele result og lave præcis 100 sammenligninger. Og hvis result er stort, som 10000, vil der være 10000 sammenligninger.

Det er ikke et problem i sig selv, fordi JavaScript-motorer er meget hurtige, så det at gennemgå et array med 10000 elementer kun tager mikrosekunder.

Men vi laver sådan en test for hvert element i arr i for-løkken.

Så hvis arr.length er 10000, vil vi have noget i retning af 10000*10000 = 100 millioner sammenligninger. Det er meget.

Så løsningen er kun god til små arrays.

Senere i kapitlet Map og Set vil vi se, hvordan man optimerer det.

NB: I moderne JavaScript kan vi bruge en Set til at gøre det samme på en mere effektiv måde.

Åbn løsningen med tests i en sandbox.

vigtighed: 4

Lad os antage, at vi har modtaget et array af brugere i formen {id:..., name:..., age:... }.

Opret en funktion groupById(arr), der opretter et objekt ud fra det, med id som nøgle, og array-elementerne som værdier.

For eksempel:

let users = [
  {id: 'john', name: "John Smith", age: 20},
  {id: 'ann', name: "Ann Smith", age: 24},
  {id: 'pete', name: "Pete Peterson", age: 31},
];

let usersById = groupById(users);

/*
efter kaldet skal vi have:

usersById = {
  john: {id: 'john', name: "John Smith", age: 20},
  ann: {id: 'ann', name: "Ann Smith", age: 24},
  pete: {id: 'pete', name: "Pete Peterson", age: 31},
}
*/

Sådan en funktion er virkelig praktisk, når man arbejder med serverdata.

I denne opgave antager vi at id er unik. Der må ikke være to array-elementer med samme id.

Brug array-metoden .reduce i løsningen.

Åbn en sandbox med tests.

function groupById(array) {
  return array.reduce((obj, value) => {
    obj[value.id] = value;
    return obj;
  }, {})
}

Åbn løsningen med tests i en sandbox.

Tutorial-oversigt

Kommentarer

læs dette før du kommenterer…
  • Hvis du har forslag til forbedringer - så opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
  • Hvis du ikke forstår noget i artiklen - så uddyb venligst.
  • For at indsætte få ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen…)