Instantly share code, notes, and snippets.

Embed
What would you like to do?
Array iteration methods summarized

While attempting to explain JavaScript's reduce method on arrays, conceptually, I came up with the following - hopefully it's helpful; happy to tweak it if anyone has suggestions.

Intro

JavaScript Arrays have lots of built in methods on their prototype. Some of them mutate - ie, they change the underlying array in-place. Luckily, most of them do not - they instead return an entirely distinct array. Since arrays are conceptually a contiguous list of items, it helps code clarity and maintainability a lot to be able to operate on them in a "functional" way. (I'll also insist on referring to an array as a "list" - although in some languages, List is a native data type, in JS and this post, I'm referring to the concept. Everywhere I use the word "list" you can assume I'm talking about a JS Array) This means, to perform a single operation on the list as a whole ("atomically"), and to return a new list - thus making it much simpler to think about both the old list and the new one, what they contain, and what happened during the operation.

Below are some of the methods that iterate - in other words, that operate on the entire list, one item at a time. When you call them, you provide a callback function - a single function that expects to operate on one item at a time. Based on the Array method you've chosen, the callback gets specific arguments, and may be expected to return a certain kind of value - and (except for forEach) the return value determines the final return value of the overarching array operation. Although most of the methods are guaranteed to execute for each item in the array - for all of them - some of the methods can stop iterating partway through; when applicable, this is indicated below.

All array methods iterate in what is traditionally called "left to right" - more accurately (and less ethnocentrically) from index 0, to index length - 1 - also called "start" to "end". reduceRight is an exception in that it iterates in reverse - from end to start.


forEach:

  • callback answers: here’s an item. do something nutty with it, i don't care what.
  • callback gets these arguments: item, index, list
  • final return value: nothing - in other words, undefined
  • example use case:
[1, 2, 3].forEach(function (item, index) {
  console.log(item, index);
});

map:

  • callback answers: here’s an item. what should i put in the new list in its place?
  • callback gets these arguments: item, index, list
  • final return value: list of new items
  • example use case:
const three = [1, 2, 3];
const doubled = three.map(function (item) {
  return item * 2;
});
console.log(three === doubled, doubled); // false, [2, 4, 6]

filter:

  • callback is a predicate - it should return a truthy or falsy value
  • callback answers: should i keep this item?
  • callback gets these arguments: item, index, list
  • final return value: list of kept items
  • example use case:
const ints = [1, 2, 3];
const evens = ints.filter(function (item) {
  return item % 2 === 0;
});
console.log(ints === evens, evens); // false, [2]

reduce:

  • callback answers: here’s the result from the previous iteration. what should i pass to the next iteration?
  • callback gets these arguments: result, item, index, list
  • final return value: result of last iteration
  • example use case:
// NOTE: `reduce` and `reduceRight` take an optional "initialValue" argument, after the reducer callback.
// if omitted, it will default to the first item.
const sum = [1, 2, 3].reduce(function (result, item) {
  return result + item;
}, 0); // if the `0` is omitted, `1` will be the first `result`, and `2` will be the first `item`

reduceRight: (same as reduce, but in reversed order: last-to-first)

some:

  • callback is a predicate - it should return a truthy or falsy value
  • callback answers: does this item meet your criteria?
  • callback gets these arguments: item, index, list
  • final return value: true after the first item that meets your criteria, else false
  • note: stops iterating once it receives a truthy value from your callback.
  • example use case:
const hasNegativeNumbers = [1, 2, 3, -1, 4].some(function (item) {
  return item < 0;
});
console.log(hasNegativeNumbers); // true

every:

  • callback is a predicate - it should return a truthy or falsy value
  • callback answers: does this item meet your criteria?
  • callback gets these arguments: item, index, list
  • final return value: false after the first item that failed to meet your criteria, else true
  • note: stops iterating once it receives a falsy value from your callback.
  • example use case:
const allPositiveNumbers = [1, 2, 3].every(function (item) {
  return item > 0;
});
console.log(allPositiveNumbers); // true

find:

  • callback is a predicate - it should return a truthy or falsy value
  • callback answers: is this item what you’re looking for?
  • callback gets these arguments: item, index, list
  • final return value: the item you’re looking for, or undefined
  • note: stops iterating once it receives a truthy value from your callback.
  • example use case:
const objects = [{ id: 'a' }, { id: 'b' }, { id: 'c' }];
const found = objects.find(function (item) {
  return item.id === 'b';
});
console.log(found === objects[1]); // true

findIndex:

  • callback is a predicate - it should return a truthy or falsy value
  • callback answers: is this item what you’re looking for?
  • callback gets these arguments: item, index, list
  • final return value: the index of the item you’re looking for, or -1
  • note: stops iterating once it receives a truthy value from your callback.
  • example use case:
const objects = [{ id: 'a' }, { id: 'b' }, { id: 'c' }];
const foundIndex = objects.findIndex(function (item) {
  return item.id === 'b';
});
console.log(foundIndex === 1); // true
@ljharb

This comment has been minimized.

Show comment
Hide comment
@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Dec 20, 2016

My preferred mnemonic for the order of the callback arguments is "Element, Index, Container", because EIC is the first three letters of Brendan EICH.

(Serendipitously, there's been a long-standing request for a way to force an iteration method to break, without having to throw. One of the proposed methods is a unique value passed to the callback that, if returned by the callback, stops the iteration. This would then add "Halt" to the mnemonic, fully spelling out EICH. ^_^)

tabatkins commented Dec 20, 2016

My preferred mnemonic for the order of the callback arguments is "Element, Index, Container", because EIC is the first three letters of Brendan EICH.

(Serendipitously, there's been a long-standing request for a way to force an iteration method to break, without having to throw. One of the proposed methods is a unique value passed to the callback that, if returned by the callback, stops the iteration. This would then add "Halt" to the mnemonic, fully spelling out EICH. ^_^)

@ljharb

This comment has been minimized.

Show comment
Hide comment
@xgrommx

This comment has been minimized.

Show comment
Hide comment
@xgrommx

xgrommx Dec 21, 2016

@ljharb in your last sample should be findIndex

xgrommx commented Dec 21, 2016

@ljharb in your last sample should be findIndex

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Dec 21, 2016

@xgrommx thanks, fixed

Owner

ljharb commented Dec 21, 2016

@xgrommx thanks, fixed

@konamax123

This comment has been minimized.

Show comment
Hide comment
@konamax123

konamax123 Jan 13, 2017

This is great! Very easy to understand. Thank you

konamax123 commented Jan 13, 2017

This is great! Very easy to understand. Thank you

@m2mathew

This comment has been minimized.

Show comment
Hide comment
@m2mathew

m2mathew Jan 13, 2017

Wonderful! Thanks for taking time to make this.

m2mathew commented Jan 13, 2017

Wonderful! Thanks for taking time to make this.

@emetselaar

This comment has been minimized.

Show comment
Hide comment
@emetselaar

emetselaar Jan 13, 2017

Great explanation. You could put this straight into a Learn JS book.

emetselaar commented Jan 13, 2017

Great explanation. You could put this straight into a Learn JS book.

@HashemKhalifa

This comment has been minimized.

Show comment
Hide comment
@HashemKhalifa

HashemKhalifa Jan 13, 2017

Thanks, I was confused and now everything is very easy to understand 👍

HashemKhalifa commented Jan 13, 2017

Thanks, I was confused and now everything is very easy to understand 👍

@ggauravr

This comment has been minimized.

Show comment
Hide comment
@ggauravr

ggauravr Jan 13, 2017

@ljharb Good list. sort might be a useful addition, since it takes a comparator and iterates over all the elements. Just added that in my fork of the gist https://gist.github.com/ggauravr/a41a0e94f314623e86f55d425a7b0f5d, if you want to add.

ggauravr commented Jan 13, 2017

@ljharb Good list. sort might be a useful addition, since it takes a comparator and iterates over all the elements. Just added that in my fork of the gist https://gist.github.com/ggauravr/a41a0e94f314623e86f55d425a7b0f5d, if you want to add.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jan 14, 2017

@ggauravr I intentionally omitted that one, because sort mutates.

Owner

ljharb commented Jan 14, 2017

@ggauravr I intentionally omitted that one, because sort mutates.

@yogx4u

This comment has been minimized.

Show comment
Hide comment
@yogx4u

yogx4u Jan 17, 2017

Very good explanation. Also I didn't knew of reverse iteration like in case of reduceRight. Thank you for that. 👍

yogx4u commented Jan 17, 2017

Very good explanation. Also I didn't knew of reverse iteration like in case of reduceRight. Thank you for that. 👍

@felisio

This comment has been minimized.

Show comment
Hide comment
@felisio

felisio commented Jan 18, 2017

Hi, I'm forked and translate for Portugues-Brazil

https://gist.github.com/felisio/4eb4d427951b26e3d1616a411e93df87

@sAbakumoff

This comment has been minimized.

Show comment
Hide comment

sAbakumoff commented Jan 19, 2017

@azl397985856

This comment has been minimized.

Show comment
Hide comment
@azl397985856

azl397985856 Jan 19, 2017

This is great! Very easy to understand. Thank you

azl397985856 commented Jan 19, 2017

This is great! Very easy to understand. Thank you

@bestwestern

This comment has been minimized.

Show comment
Hide comment
@bestwestern

bestwestern Jan 19, 2017

Thanks - very good.

  1. They could delete every or some, couldn't they? (By negating the criteria you could get the other)
  2. Are they speedy? (forEach vs for(var i=0;i<_.length;i++){})

bestwestern commented Jan 19, 2017

Thanks - very good.

  1. They could delete every or some, couldn't they? (By negating the criteria you could get the other)
  2. Are they speedy? (forEach vs for(var i=0;i<_.length;i++){})
@ddanielbee

This comment has been minimized.

Show comment
Hide comment
@ddanielbee

ddanielbee Jan 19, 2017

@Jiharb This is great. Something else that could help people wanting some more beef about the feature is a link to its MDN page.

ddanielbee commented Jan 19, 2017

@Jiharb This is great. Something else that could help people wanting some more beef about the feature is a link to its MDN page.

@Bandito11

This comment has been minimized.

Show comment
Hide comment
@Bandito11

Bandito11 Jan 19, 2017

This awesome, thanks!

Bandito11 commented Jan 19, 2017

This awesome, thanks!

@icanhazstring

This comment has been minimized.

Show comment
Hide comment
@icanhazstring

icanhazstring Jan 19, 2017

Thanks man. Really good explanation

icanhazstring commented Jan 19, 2017

Thanks man. Really good explanation

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Jan 19, 2017

@bestwestern yes, they are speedy, but performance is the least important thing to worry about when writing code - clarity is the most. Both every and some are needed to clearly express your intention, which is the whole point. You could implement all of these with reduce alone, but that wouldn't be as clear.

Owner

ljharb commented Jan 19, 2017

@bestwestern yes, they are speedy, but performance is the least important thing to worry about when writing code - clarity is the most. Both every and some are needed to clearly express your intention, which is the whole point. You could implement all of these with reduce alone, but that wouldn't be as clear.

@eliortabeka

This comment has been minimized.

Show comment
Hide comment
@eliortabeka

eliortabeka Jan 19, 2017

This is awesome man, Thanks!

eliortabeka commented Jan 19, 2017

This is awesome man, Thanks!

@NaiyaShah-BTC

This comment has been minimized.

Show comment
Hide comment
@NaiyaShah-BTC

NaiyaShah-BTC Jan 31, 2017

Superb!! Nice efforts

NaiyaShah-BTC commented Jan 31, 2017

Superb!! Nice efforts

@mr-devboy

This comment has been minimized.

Show comment
Hide comment
@mr-devboy

mr-devboy Feb 13, 2017

Always used this methods. But not all supported by IE

mr-devboy commented Feb 13, 2017

Always used this methods. But not all supported by IE

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Feb 14, 2017

@mr-devboy that's where the es5-shim and es6-shim come in. I recommend every site always use them.

Owner

ljharb commented Feb 14, 2017

@mr-devboy that's where the es5-shim and es6-shim come in. I recommend every site always use them.

@vladyn

This comment has been minimized.

Show comment
Hide comment
@vladyn

vladyn Apr 2, 2017

Very nifty. Thank you!

vladyn commented Apr 2, 2017

Very nifty. Thank you!

@OMENSAH

This comment has been minimized.

Show comment
Hide comment
@OMENSAH

OMENSAH commented Apr 18, 2017

Nice one

@andrey-smolko

This comment has been minimized.

Show comment
Hide comment
@andrey-smolko

andrey-smolko May 4, 2017

Great work!
I think it is also worth to mention that there is a second optional parameter in addition to callback for map(), forEach() and several others:
(callback[, thisArg])
thisArg - this inside callback function.

andrey-smolko commented May 4, 2017

Great work!
I think it is also worth to mention that there is a second optional parameter in addition to callback for map(), forEach() and several others:
(callback[, thisArg])
thisArg - this inside callback function.

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb May 14, 2017

@andrey-smolko I've intentionally omitted that; with ES6 arrow functions there's really zero point in using the thisArg ever again.

Owner

ljharb commented May 14, 2017

@andrey-smolko I've intentionally omitted that; with ES6 arrow functions there's really zero point in using the thisArg ever again.

@derek-knox

This comment has been minimized.

Show comment
Hide comment
@derek-knox

derek-knox Jan 18, 2018

@ljharb I think it's worth delving into reduceRight() just like the other methods. Just mentioning that it's the opposite order of reduce() isn't helpful (especially when the reduce() example is a summation). Where's the value in summing in reverse order?

derek-knox commented Jan 18, 2018

@ljharb I think it's worth delving into reduceRight() just like the other methods. Just mentioning that it's the opposite order of reduce() isn't helpful (especially when the reduce() example is a summation). Where's the value in summing in reverse order?

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Feb 24, 2018

@derek-knox i mean, where's the value in summing in any order? "summing" isn't the only or the primary benefit of reduce. It really just depends on what data you're reducing, what order it's in, what you're reducing it to, and which order makes the most sense to do so.

Most examples that would demonstrate more advanced uses of reduce would be great, but they might be a bit distracting for this basic explainer.

Owner

ljharb commented Feb 24, 2018

@derek-knox i mean, where's the value in summing in any order? "summing" isn't the only or the primary benefit of reduce. It really just depends on what data you're reducing, what order it's in, what you're reducing it to, and which order makes the most sense to do so.

Most examples that would demonstrate more advanced uses of reduce would be great, but they might be a bit distracting for this basic explainer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment