Looping in JS - all the ways to iterate
Last updated on April 2021
Here is a list of everything I can think of relating to looping in JS.
There isn't much purpose to this post, I was just trying to find/think of all the ways to do anything related to loop/iterating.
I've ordered it by the most common/most useful at the start. Some are definitely loops, but as you scroll further down they're harder to say they're loops and more just using array-like functions.
I also start off with just normal for loops with arrays or for loops... but further down revisit it with other iterables, basic recursive functions etc.
Some are not typical ways of looping (e.g. is Array.some()
considered a way to loop? Only technically...)
simple for loop
Most common is probably a for loop. This is the old classic for loop.
for(let i = 0; i < 5; i++) {
console.log(i)
}
// outputs 0 - 4
You can make it an infinite loop:
for (let i = 0; ; i++) {
if(i >= 5) break; // remove this for infinite loop
console.log(i)
}
// outputs 0 -4
- The keyword
break
will break out of the for loop. - the keyword
continue
will continue to the next iteration of the loop
for in loop
get the keys of an object:
const dict = {
key1: 'val1',
key2: 'val2',
key3: 'val3'
}
for(const k in dict) {
console.log(k);
}
// 'key1'
// 'key2'
// 'key3'
for of loop
Use a for of loop to get the values in an array
technically: get it from an iterable - more on these later
const vals = ['a', 'b', 'c', 'd'];
for(const v of vals) {
console.log(v)
}
while loop
let i = 0;
while(i < 5) {
console.log(i);
i++;
}
// outputs 0-4
Can also do things like this:
const arr = [5, 10, 20];
while (arr.length > 0) {
console.log(arr.shift());
}
do loop
Similar to a while loop, but when you need to always run it at least once. (The condition check is run after each iteration to see if it can continue)
let i = 999;
do{
console.log(i)
}
while(i < 5)
// outputs just 999, and only runs the code inside do{} once
Array.map()
Use map to take an array, and transform each value to something else.
const vals = ['a', 'b', 'c', 'd'];
const newVals = vals.map(v => v.toUpperCase());
console.log(newVals) // [ 'A', 'B', 'C', 'D' ]
Array.forEach()
Use forEach in a very similar way to Array.map()
, but use it when you don't need to create a new array.
(It returns undefined)
Useful if you want to run some function that has side effects.
const vals = ['a', 'b', 'c', 'd'];
vals.forEach(v => console.log(v))
// logs each letter
Array.reduce()
Take an array, reduce it down to one value.
(That value can be another array).
There are 2 main args to the function - acc, cur
(accumulative, current).
const vals = [50, 100, 200]
// 2 args:
// - a callback (acc, cur, index)
// - optional 2nd arg which is the initial value. In most cases you should set initial value. Otherwise it uses the first item in the array
const sum = vals.reduce((acc, cur) => acc + cur, 0)
const sum2 = vals.reduce((acc, cur) => acc + cur) // don't need initial value here for things like sum
console.log(sum); // 350
console.log(sum2); // 350
// this would be easier with .map()
const newVals = vals.reduce((acc, cur) => {
return [...acc, cur * 2]
}, []) // need 2nd param here to build up a new array
console.log(newVals) // [ 100, 200, 400 ]
// this would be easier with .filter()
const onlyLargeNumbers = vals.reduce((acc, cur) => {
if(cur >= 100) return [...acc, cur]
return acc
}, [])
console.log(onlyLargeNumbers) // [ 100, 200 ]
There is also .reduceRight()
which works in reverse order (starting at the end of the array instead of at the start).
Array.filter()
You use filter to take in an array, and return a new array with certain items removed.
const vals = [50, 100, 200]
const largeNums = vals.filter(num => num >= 100)
console.log(largeNums); // [ 100, 200 ]
Array.every()
Use .every()
to return a boolean if everything in an array matches a condition.
const vals = [2, 4, 8, 10, 12,]
const allEven = vals.every(num => num % 2 === 0)
console.log(allEven) // true
const allLargerThan5 = vals.every(num => num > 5)
console.log(allLargerThan5) // false
Array.some()
Use Array.some()
to return true if the function you pass returns true for at least one
const vals = [2, 4, 8, 10, 12,]
const atLeastOneEven = vals.some(num => num % 2 === 0)
console.log(atLeastOneEven) // true
const atLeastOneLargerThan5 = vals.some(num => num > 5)
console.log(atLeastOneLargerThan5) // true
Array.includes()
Pass in a value and it returns true/false if an array contains that value.
It is similar to .find()
, but in .find()
you pass a function that is run on every object. With .includes()
it just does a equality check
const animals = ['snake', 'horse', 'cat'];
console.log(animals.includes('snake')); // true
console.log(animals.includes('dog')); // false
// note:
const animals2 = [
{type: 'snake'},
{type: 'horse'}
]
console.log(animals.includes({type: 'snake'})) // false, as it compares the objects with are different
Array.indexOf()
Returns the index in an array of an object/value, or -1 if not found.
It returns the first index where it finds it
This is similar to .findIndex()
, but in findIndex()
you pass in a function that returns true/false. In .indexOf()
it just does an equality check.
const animals = ['snake', 'horse', 'cat'];
console.log(animals.indexOf('cat')); // 2
console.log(animals.indexOf('dog')); // -1
// note:
const animals2 = [
{type: 'snake'},
{type: 'horse'}
]
console.log(animals.indexOf({type: 'snake'})) // -1, as it compares the objects with are different
Array.lastIndexOf()
Use .lastIndexOf()
just like .indexOf
but instead of starting from the start of the index, it starts at the end
const animals = ['snake', 'horse', 'cat', 'horse', 'dog'];
console.log(animals.indexOf('horse')); // 1
console.log(animals.lastIndexOf('horse')); // 3
Array.find()
Find first item in an array that matches a condition
const vals = [2, 4, 8, 10, 12,]
const firstBiggerThan5 = vals.find(num => num > 5)
console.log(firstBiggerThan5) // 8
const firstBiggerThan50 = vals.find(num => num > 50)
console.log(firstBiggerThan50) // undefined
Array.findIndex()
Similar to .find()
but returns the index (or -1 if not found)
const vals = [2, 4, 8, 10, 12,]
const firstBiggerThan5 = vals.findIndex(num => num > 5)
console.log(firstBiggerThan5) // 2
const firstBiggerThan50 = vals.findIndex(num => num > 50)
console.log(firstBiggerThan50) // -1
Array.flat()
Use .flat()
to flatten an array of arrays.
const allCases = [
['hi', 'HI', 'Hi'],
['bye', 'BYE', 'By']
]
console.log(allCases.flat())
// [ 'hi', 'HI', 'Hi', 'bye', 'BYE', 'By' ]
Array.flatMap()
Similar to Array.map()
except it will flatten it if it contains arrays
const words = ['hi', 'bye'];
const allCases = words.flatMap(plainWord => [plainWord, plainWord.toUpperCase(), plainWord.toLowerCase()])
console.log(allCases)
// [ 'hi', 'HI', 'hi', 'bye', 'BYE', 'bye' ]
Array.sort()
This isn't a normal way to iterate over an array... but it does. (and will read items in the array more than once as it sorts).
const vals = [123, 456, 1, 2, 3, 1000, 2000, 1, 1, 3]
// .sort() sorts in place and mutates original. good practice to
// always leave original alone, so we start with a spread...
const sorted = [...vals].sort((a, b) => {
console.log(`Comparing ${a} <> ${b}`)
if(a > b) return 1
if(a < b) return -1
return 0
})
console.log(sorted)
/*
'Comparing 456 <> 123'
'Comparing 1 <> 456'
'Comparing 1 <> 456'
'Comparing 1 <> 123'
'Comparing 2 <> 123'
// truncated ...
'Comparing 1 <> 2'
'Comparing 1 <> 1'
'Comparing 3 <> 3'
'Comparing 3 <> 1000'
'Comparing 3 <> 456'
'Comparing 3 <> 123'
Final output
[ 1, 1, 1, 2, 3, 3, 123, 456, 1000, 2000 ]
*/
In chrome it uses timsort which is O(n log n)
.
Array.entries()
I rarely see this in use but it is nice if you want key/value pairs for every i/itemndex in an array:
const arrayOfNums = [5, 10, 20];
for (let [index, value] of arrayOfNums.entries()) {
console.log(index, value);
}
/*
0 5
1 10
2 20
*/
Array.keys()
Gets keys from an array.
const arrayOfNums = [5, 10, 20];
const keys = arrayOfNums.keys()
console.log([...keys]) // [ 0, 1, 2 ]
note the difference between Array.keys()
and Object.keys()
on a sparse array:
// comparison to Object.keys() on an array:
const arr = ["a", , "c"]; // note the `, ,` to generate a sparse array
const sparseKeys = Object.keys(arr);
const denseKeys = [...arr.keys()];
console.log(sparseKeys); // ['0', '2']
console.log(denseKeys); // [0, 1, 2]
Array.from()
You can create a new array, and use .from()
to loop through the new items in that array.
const newArray = Array.from({length: 5}, (x, i) => i)
console.log(newArray) // [ 0, 1, 2, 3, 4 ]
const newArrayOfAs = Array.from({length: 5}, () => 'a')
console.log(newArrayOfAs) // [ 'a', 'a', 'a', 'a', 'a' ]
Looping over objects
You can use some functions on Object
to get keys, values and entries to then loop over those.
const myObject = {
k1: 'v1',
k2: 'v2'
}
const keys = Object.keys(myObject);
keys.forEach(k => console.log(k)) // 'k1', 'k2'
keys.forEach(k => console.log(myObject[k])); // 'k1', 'k2'
const values = Object.values(myObject)
values.forEach(v => console.log(v)) // 'v1', 'v2'
const entries = Object.entries(myObject)
console.log(entries[0]); // ['k1', 'v1']
entries.forEach(kv => {
console.log(`myObject[${kv[0]}] has value of ${kv[1]}`)
})
// 'myObject[k1] has value of v1'
// 'myObject[k2] has value of v2'
Loop over generators
const generator = function* () {
console.log("starting")
yield 'a';
console.log("between a-b")
yield 'b';
console.log("between b-c")
yield 'c';
console.log("end")
}
for (const val of generator()) {
console.log(val)
}
/*
'starting'
'a'
'between a-b'
'b'
'between b-c'
'c'
'end'
*/
You can also spread it to get an array (to use things like .map()
)
const generator = function* () {
yield 'a';
yield 'b';
yield 'c';
}
const asArray = [...generator()]
asArray.forEach(v => console.log(v))
/*
'a'
'b'
'c'
*/
Loop over iterables
Normally you cannot loop over plain old javascript objects like this:
const myObject = {
k1: 'v1',
k2: 'v2'
}
for(const val of myObject){
console.log(val) // TypeError: myObject is not iterable
}
But if you set the special property Symbol.iterator
, then you can!
const myObject = {
*[Symbol.iterator]() {
yield this.k1
yield this.k2
},
k1: 'v1',
k2: 'v2'
}
for(const val of myObject){
console.log(val)
}
/*
outputs:
'v1'
'v2'
*/
or combine it with something like Object.keys(this)
:
const myObject = {
*[Symbol.iterator]() {
const allKeys = Object.keys(this)
for(const key of allKeys) {
yield this[key]
}
},
k1: 'v1',
k2: 'v2'
}
for(const val of myObject){
console.log(val); // 'v1', 'v2'
}
Spreading
This isn't really iterating, but if an object is iterable then you can spread it.
const hello = 'hey';
const split = [...hello];
console.log(split); // [ 'h', 'e', 'y' ]
const ages = [10, 20, 30, 40];
const agesClone = [...ages]
console.log(ages, agesClone) // [ 10, 20, 30, 40 ] [ 10, 20, 30, 40 ]
console.log(ages === agesClone) // false
console.log(JSON.stringify(ages) === JSON.stringify(agesClone)) // true
const agesSet = new Set([1,2,3]);
agesSet.add(4)
const asArray = [...agesSet]
console.log(asArray) // [ 1, 2, 3, 4 ]
Splitting strings
Similar to above on the string, you can use .split()
to turn a string to an array. This is a stretch calling it a loop/iterating over it, but maybe it counts.
const hi = 'hello'
const letters = hi.split('')
console.log(letters) // [ 'h', 'e', 'l', 'l', 'o' ]
Loop over DOM elements
If using JS in a browser, there are some things that you can loop over. They often only have .forEach()
but they're iterable so you can use the spread operator to get a proper array.
document.querySelectorAll('div').forEach(el => console.log(el))
const asArray = [...document.querySelectorAll('div')];
const texts = asArray.map(el => el.textContent)
const inputs = document.querySelectorAll('input');
for (const input of inputs) {
console.log(input.getAttribute('value')
}
querySelectorAll()
returns aNodeList
NodeList
has.forEach()
,.entries()
,.keys()
,.values()
, and a.length
property but it is not an array.- Convert to an array by spreading
[... result]
orArray.from(result)
querySelectorAll()
returns a staticNodeList
. There are also liveNodeList
s, which means that changes in the DOM automatically update the collection.
Loop over Sets
Similar to DOM elements, Sets are iterable and have some functions like .forEach
... but you can spread it to get a normal array
const words = new Set()
words.add('hi')
words.add('bye')
words.forEach(w => console.log(w)); // 'hi', 'bye'
const asArray = [...words]
const upperCase = asArray.map(w => w.toUpperCase())
console.log(upperCase) // [ 'HI', 'BYE' ]
Loop with recursion
This is cheating a bit, but you can iterate through items with recursion
function countFromTo(start, max, cur = start) {
if(cur > max) return
console.log(cur)
countFromTo(start, max, cur + 1)
}
countFromTo(10, 15) // 10 11 12 13 14 15
Or iterate through an array using recursion:
function rec(vals) {
if(!vals.length) return
const [first, ...rest] = vals
console.log(first);
rec(rest)
}
rec(['a', 'b', 'c'])
// outputs:
// 'a'
// 'b'
// 'c'
const vals2 = ['a', 'b', 'c']
function rec2(index) {
if(index === vals2.length ) return;
console.log(vals2[index])
rec2(index+1)
}
rec2(0)
// 'a', 'b', 'c'
Convert array to a string
Not sure this really counts as a loop, but maybe...
const arr = [1, 2, 3, 4, 5];
const asStr = arr.toString()
console.log(asStr)
const asStr2 = arr.join()
console.log(asStr2)
Use .apply() to use all items in an array as arguments for a function
You can call a function, and pass in an array which is used for each argument to call that function. (And sets this
- but in my demo I ignore that)
This barely counts as a way to loop, but it does use arrays...
const arr = [5, 10, 20];
function printThreeVars(a, b, c) {
console.log(a, b, c);
}
printThreeVars.apply(null, arr);
// 5 10 20
Loop with recursive timeout
This is really just a recursive function, very similar to the previous recursive idea to iterate through multiple calls...
// loop with setTimeout
let max = 5
function processNumber(i) {
console.log(i)
if(i > max) return;
setTimeout(() => processNumber(i + 1), 0)
}
processNumber(0)
Math.min and other functions that you can spread an array into
Are these looping/iterating? Interally they are as they go through all arguments passed in... I don't think it really counts as a loop/iterator though.
const vals = [1, 4, 5, 9]
const max = Math.max(...vals)
const min = Math.min(...vals)
console.log({max, min}); // { max: 9, min: 1 }
Looping through DOM array-like objects
I've mentioned NodeList
which is array-like.
There are other array-like objects, so they are easy to loop/iterate over. Most are part of the web api.
Here is a list of some of them.
strings
- strings are array-like.
- They have
.length
, you can loop over them, you can spread them
HTMLCollection
- HTMLCollection are lists of elements in the DOM.
- You can get them with calls to functions like
document.getElementsByClassName('your-class')
FileList
- You get a FileList when getting an input that has
type='file'
- It has
.length
so you can do a normalfor
loop - For example if you have
<input id="fileItem" type="file" />
, thendocument.getElementById('fileItem')
is a FileList, which is array-like
DOMTokenList
Get this when looking at classlist
on an element.
For example:
document.querySelector('body').classList.forEach(c => console.log(c))
TypedArray
(not DOM/web API only)
Things like Uint8Array
(and related) and in NodeJS Buffer
, extend TypedArray
.
You can call things like .forEach
on these:
const nums = new Uint8Array([10, 20, 30]);
nums.forEach((item) => console.log(item))
Ok, that is everything I could come up with related to looping or iterating in JS. Get in touch if you can think of anything I missed.