Hoppa till huvudinnehåll

Index av största värdet i en array

· 4 min att läsa
Filip Tammergård
Programmerare på Frilans Finans

Att hitta indexet av det största värdet i en array är en av de där uppgifterna som verkar trivial men har flera olika lösningar – var och en med sina avvägningar kring läsbarhet, prestanda och fallgropar.

TL;DR

  • En for-loop fungerar, men kräver flera muterbara variabler.
  • En reduce kan lösa det på ett enda varv, men är inte särskilt lättläst.
  • I praktiken är years.indexOf(Math.max(...years)) oftast det bästa valet – lättläst och kompakt, även om datan i praktiken gås igenom tre gånger bakom kulisserna.

Hitta indexet av det största värdet

I de kommande exemplen utgår jag från den här arrayen med årtal:

const years = [1941, 1714, 2005, 1998, 1871, 2001, 1700]

Hur gör vi om vi vill få ut indexet av det största värdet i arrayen?

Alternativ 1: for-loop (ett dåligt alternativ)

Ett vanligt angreppssätt är att ta till en vanlig for-loop:

let max = years[0]
let maxIndex = 0

for (let i = 1; i < years.length; i++) {
if (years[i] > max) {
maxIndex = i
max = years[i]
}
}

// maxIndex = 2

Det funkar, men lösningen lutar sig på flera muterbara variabler (max, maxIndex, i) – och i finns bara till för att driva loopen, vilket är precis den sortens bokföring som moderna array-metoder låter oss slippa.

Alternativ 2: reduce (ett klurigt alternativ)

For-loopen kan vikas ihop till ett enda reduce-anrop som går igenom arrayen ett varv och bär med sig det löpande max-indexet i ackumulatorn:

const maxYearIndex = years.reduce(
(maxIndex, curr, index) => (curr > years[maxIndex] ? index : maxIndex),
0,
)

// maxYearIndex = 2

Inga muterbara hjälpvariabler längre, och arrayen loopas bara igenom en gång. Nackdelen är att man får läsa callbacken noggrant för att förstå vad som händer – maxIndex är ackumulatorn, curr och index är det nuvarande värdet och dess position, och years[maxIndex] är det nuvarande max-värdet.

Alternativ 3: Math.max + indexOf (det bästa alternativet)

Det mest läsbara sättet är att först ta fram max-värdet med Math.max och sedan slå upp dess position med indexOf:

const maxYearIndex = years.indexOf(Math.max(...years))

// maxYearIndex = 2

Hela lösningen får plats på en rad och läses nästan som vanlig text: indexet av max-värdet i years. Det är lätt att avfärda lösningen bara baserat på antal varv – spread bygger en argumentlista, Math.max läser den, och indexOf skannar originalarrayen, så datan gås i praktiken igenom tre gånger. Men för arrayer som ändå får plats i minnet är skillnaden försumbar. Både Math.max och indexOf är inbyggda och körs nativt, medan reduce-varianten anropar en JS-callback per element, så den skenbara fördelen "ett varv i stället för tre" ger sällan utslag i praktiken.

Det finns en sak att känna till: spread-steget har en hård gräns. För riktigt stora arrayer (i storleksordningen 100 000+ element) kan ...years spräcka call stack med felet "Maximum call stack size exceeded", eftersom runtimen inte får plats med så många argument på stacken. I det sällsynta fallet är reduce-varianten från Alternativ 2 ett säkrare val.

Bryt ut till en hjälpfunktion

För återanvändning kan enradaren lätt paketeras i en generisk hjälpfunktion:

function getMaxIndex(array) {
return array.indexOf(Math.max(...array))
}

const maxYearIndex = getMaxIndex(years)

// maxYearIndex = 2

För en tom array returnerar funktionen -1 på köpet: Math.max() utan argument är -Infinity, och [].indexOf(-Infinity) är -1 – ett vettigt "inget index"-värde.

Så där, nu vet du hur man hittar indexet av det största värdet i en array!

Referenser