Hoppa till huvudinnehåll

Summera tal i en array med JavaScript

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

Det finns flera sätt att räkna ut summan av tal i en array med JavaScript. De olika sätten skiljer sig framför allt i hur lättlästa de är – och lättläst kod är lättare att underhålla och lättare för andra att förstå och bygga vidare på.

TL;DR

  • Det finns många sätt att summera tal i en array, men flera sätt är svårlästa och onödigt krångliga.
  • Det bästa sättet är med hjälp av den inbyggda funktionen reduce – eller den nyare Math.sumPrecise om du kan förlita dig på att den finns tillgänglig.

Summera tal i en array

I kommande exempel utgår jag från den här arrayen med slumpmässiga tal:

const numbers = [20, 38, 23, 1, 39, 71]

Alternativ 1: manuell summering (ett uselt alternativ)

Det går att göra summeringen endast med hjälp av addition, på det här viset:

const sum =
numbers[0] + numbers[1] + numbers[2] + numbers[3] + numbers[4] + numbers[5]

// sum = 192

Det här alternativet är uselt eftersom det förutsätter vetskap om hur många tal som finns i arrayen. Om ett tal skulle läggas till i arrayen skulle summan bli fel.

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

Historiskt har summeringar i JavaScript gjorts genom att göra en for-loop på det här sättet:

let sum = 0

for (let i = 0; i < numbers.length; i++) {
sum += numbers[i]
}

// sum = 192

Det är fortfarande ett otympligt sätt att summera på. Stilen är imperativ – du måste själv styra hur loopen körs – och det krävs en indexvariabel som i vars enda uppgift är att hålla koll på position. Sådana variabler kan och bör oftast undvikas.

Alternativ 3: forEach-loop (ett okej alternativ)

forEach läser mer naturligt än en for-loop, men kräver fortfarande att man deklarerar let sum = 0 och muterar den variabeln vid varje iteration:

let sum = 0

numbers.forEach((number) => (sum += number))

// sum = 192

forEach är en higher order function som kör en callback för varje element i arrayen. Den tar bort indexvariabeln men inte muteringen.

Alternativ 4: reduce (det bästa alternativet)

Ytterligare lite snyggare är den inbyggda funktionen reduce. Den kan användas så här:

const sum = numbers.reduce((acc, number) => acc + number, 0)

// sum = 192

Funktionen reduce tar in en funktion med parametrarna acc och number, som representerar det ackumulerade värdet respektive det nuvarande värdet i arrayen numbers. Till höger om pilen definieras vad som ska göras med värdena. I första loop-varvet är värdet av acc 0 (startvärdet som definieras i andra parametern) och number är 20. Därefter sparas resultatet av acc + number till acc i nästa loop-varv.

Summera tal i mer komplexa arrayer

I många fall är arrayerna som innehåller tal som ska summeras lite mer komplexa än det tidigare exemplet. Låt oss utgå från den här arrayen som innehåller information om alla rum i en lägenhet:

const rooms = [
{ name: "Living room", isRenovated: true, area: 31 },
{ name: "Kitchen", isRenovated: false, area: 15 },
{ name: "Bedroom", isRenovated: false, area: 21 },
{ name: "Bathroom", isRenovated: true, area: 13 },
{ name: "Hall", isRenovated: false, area: 9 },
]

Låt säga att vi vill beräkna arean i hela lägenheten.

Alternativ 1: Omforma arrayen så att den blir enkel innan summering görs

Använd map för att förvandla varje rum till bara dess area, och summera sedan den resulterande arrayen av tal.

const totalArea = rooms
.map((room) => room.area)
.reduce((acc, area) => acc + area, 0)

// totalArea = 89

Alternativ 2: Plocka ut arean ur objekten direkt under summeringen

Med den andra strategin används reduce för att plocka ut arean i objekten direkt:

const totalArea = rooms.reduce((acc, room) => acc + room.area, 0)

// totalArea = 89

Av dessa tycker jag att alternativ 2 är snyggast, eftersom den både är mest kompakt och mest lättläst.

Summera med filtrering

För att göra det lite mer komplicerat kanske vi vill veta den totala arean av de rum som inte är renoverade.

Alternativ 1: Filtrera, map:a, summera

Använd filter för att behålla bara de rum som inte är renoverade, och omforma och summera sedan som tidigare.

const totalAreaOfNotRenovatedRooms = rooms
.filter((room) => !room.isRenovated)
.map((room) => room.area)
.reduce((acc, area) => acc + area, 0)

// totalAreaOfNotRenovatedRooms = 45

Alternativ 2: Filtrera, plocka ut arean direkt under summeringen

Slipp mellansteget med map och plocka ut arean direkt inuti reduce.

const totalAreaOfNotRenovatedRooms = rooms
.filter((room) => !room.isRenovated)
.reduce((acc, room) => acc + room.area, 0)

// totalAreaOfNotRenovatedRooms = 45

Alternativ 3: Filtrera med if-sats inuti reduce

Hoppa över det separata filter-anropet och grena inuti reduceraren med en if-sats för att avgöra om ett rums area ska adderas.

const totalAreaOfNotRenovatedRooms = rooms.reduce((acc, room) => {
if (!room.isRenovated) return acc + room.area
return acc
}, 0)

// totalAreaOfNotRenovatedRooms = 45

Alternativ 4: Filtrera med ternary operator i reduce

Samma logik kan uttryckas mer kompakt med en ternary operator.

const totalAreaOfNotRenovatedRooms = rooms.reduce(
(acc, room) => (room.isRenovated ? acc : acc + room.area),
0,
)

// totalAreaOfNotRenovatedRooms = 45

Jag tycker att alternativ 2 är bäst i det här fallet. Det går nästan att läsa koden som vanlig text och förstå vad som händer:

Filtrera först fram rummen som inte är renoverade, summera sedan arean i varje rum.

Bättre alternativ

JavaScript har numera en inbyggd statisk metod för precis det här: Math.sumPrecise. Den tar en iterable av tal och returnerar summan utan flyttalsprecisionsproblem:

const sum = Math.sumPrecise([20, 38, 23, 1, 39, 71])

// sum = 192

För lägenhetsexemplet kombinerar du den med map:

const totalArea = Math.sumPrecise(rooms.map((room) => room.area))

// totalArea = 89

Math.sumPrecise är ett relativt nytt tillägg, så stödet varierar fortfarande mellan miljöer – kolla Can I use för aktuellt status. Där den inte finns är reduce fortfarande ett bra val.

Så där, nu kan du allt om att summera tal i en array med JavaScript!

Referenser