Return value of forEach

# Niloy Mondal (8 years ago)

Currently, Array.prototype.forEach returns undefined. It would be more useful if it returns the array itself.

Say I have written some code like this...

const a = [1, 2, 3]
  .map(square)
  .map(plus1)
  .reduce(add);

For some reason, I am not getting the expected output. For debugging, I would like the print the values after each step. My first initial reaction is to put a forEach step in between and print the values like so...

const a = [1, 2, 3]
  .map(square)
  .forEach(x => console.log(x))
  .map(plus1)
  .reduce(add);

Unfortunately, this does not work as forEach returns undefined. I now have to comment out all the code below it. Having the plug and play behaviour for forEach would be very convenient.

# Andrea Giammarchi (8 years ago)
const a = [1, 2, 3]
  .map(square)
  .map(x => console.log(x) || x )
  .map(plus1)
  .reduce(add);
# Frankie Bagnardi (8 years ago)

That'd be a compatibility break.

If we end up getting :: though:

function logEach(){
  this.forEach((x) => console.log(x));
  return this;
}


const a = [1, 2, 3]
  .map(square)
  ::logEach()
  .map(plus1)
  .reduce(add);

You could make that a global variable so you can sprinkle it around your code in development.

Having some developer tools in the language would be nice though. I don't even think console.log is in the spec. A global like Debug.logThis for example would be a more general ::-able.

# Niloy Mondal (8 years ago)

That'd be a compatibility break.

Ugh... you mean people actually depend on forEach returning undefined (which it always does) to do further task?

I wonder what that kinda code would look like >.<

# Andrea Giammarchi (8 years ago)

Like I've written before, if you need to return an array you can use map instead of forEach

arr.map(x => console.log(x) || x)

forEach has been like this since ever so while you wonder what kind of code would look like based on the fact forEach doesn't return anything, I actually wonder how come there are still people expecting forEach to return something.

So, it would be a breaking change and actually it's not needed since you have map.

If the new Array, and for debugging purpose, is not desired, use var log = DEBUG ? x => console.log(x) || x : x => x and abuse it as much as you like.

Would any of this work?

# Eric Devine (8 years ago)

Array#map does the non-trivial operation of copying the entire array. In the example for loging to the console, this behavior is unintended. Perhaps an underscore-like tap method should be considered to be added to the Array prototype for these cases.

# Andrea Giammarchi (8 years ago)

That's usually a made-up issue ... the example code is using two maps instead of computing the entire thing once in a composed function and it already creates multiple copies of the initial Array.

Anyway, having var log = DEBUG ? x => console.log(x) || x : x => x in

means you could do this:

const a = log([1, 2, 3]
  .map(square))
  .map(plus1)
  .reduce(add);

but again, we are using multiple maps and a reduce so if we want to be realistic, and it's for debugging purpose only, I think using map would be just fine.

# Kris Kowal (8 years ago)

forEach should return undefined. Array-like methods for iterators solve the problem of building unnecessary memory pressure for intermediate maps.

The return value of forEach is special. True, for arrays it is always undefined. However, the forEach method on an iterator returned by a generator function should be the return value of the generator. Consider this range generator that returns the number of visited values.

function *range(start, stop, step) {
    for (var index = start; index < stop; stop += step) {
        yield index;
    }
    return (stop - start) / step;
}
var iterator = range(0, 10, 1);
var iterations = iterator.forEach((x) => console.log(x));

console.log(iterations);

Kris Kowal

# Isiah Meadows (8 years ago)

What about a tap function similar to Bluebird's tap function for its promises?

I frequently write a pipe function that deals with this common case (with arrays, but frequently promises and other monadic structures as well).

// Complete transparent wrapper
function pipe(f) {
  function g(x) {
    f.apply(this, arguments)
    return x
  }

  return Object.defineProperty(g, "length",
    Object.getOwnPropertyDescriptor(f, "length"))
}

// My usual lazy version
function pipe(f) {
  return function (x) {
    f.apply(this, arguments)
    return x
  }
}

// My super-lazy version
const pipe = f => x => (f(x), x)
# Yongxu Ren (8 years ago)

I like the idea, I don't see why it might break compatibility and this is also faster than map if list is immutable.

# Andrea Giammarchi (8 years ago)

The "complete" version might look more like the following:

function pipe(f) {
  return Object.defineProperties(
    function (x) {
      f.apply(this, arguments);
      return x;
    },
    {
      length: Object.getOwnPropertyDescriptor(f, "length"),
      name: Object.getOwnPropertyDescriptor(f, "name")
    }
  );
}

yet I find this more complicated than just a map and if it is about immutable then map wins even more.

I don't see why this is a problem in October 2015, Array#forEach has been like that since ever.

Let's move on or maybe discuss a new method?

# Isiah Meadows (8 years ago)

It was merely a mention of my work around, and wasn't intended to be perfect (pipe(f) !== f). Note the lazy one-liner. I think both Array and Promise both could use this, though. And what do you all think about the name tap, for effectively tapping into the pipeline? I would love to see this make it in. It's very easily optimizable.

# Alexander Jones (8 years ago)

You mean to say you don't have

var undefined = [].forEach(Array.prototype.forEach.call);

at the top of every file!?

# Isiah Meadows (8 years ago)

Lol...not necessary in strict mode (which I thankfully work almost purely in by default) ;)

But I myself occasionally depend on that behavior as well, that of forEach returning undefined. I've found that it's not that hard to band-aid .map when I need to actually reuse the value (which isn't the usual case). I also use the same function for Promises, which I need it more frequently for.

function pipe(f) {
  return function (x) {
    f.apply(this, arguments)
    return x
  }
}

// Example
const numberRead = books
  .map(title => new Book(title))
  .map(pipe(book => console.log(`Book info: ${JSON.stringify(book)}`)))
  .filter(book => book.read)
  .length

// With my `.tap` idea
const numberRead = books
  .map(title => new Book(title))
  .tap(book => console.log(`Book info: ${JSON.stringify(book)}`))
  .filter(book => book.read)
  .length
# Naveen Chawla (7 years ago)

In that case, there should be a new function:

e.g. "forEvery"

that does exactly the same as "forEach" but returns the array.

This is a much needed feature for those of us that don't want to create a new array on each invocation but still have the ability to transform the contents of the array (or do some other processing) from within a .sort / .map / .filter etc. chain.

Otherwise, in complicated transformations, we have to end the chain, perform the forEach separately, and then resume the chain, making the code more messy. Sort is an example of a function that already allows mutation of the existing array and returns it, so this would be nothing extraordinary for ES.

# Sebastian Malton (7 years ago)

An HTML attachment was scrubbed... URL: esdiscuss/attachments/20170706/618947df/attachment

# Naveen Chawla (7 years ago)
# Michael J. Ryan (7 years ago)

Woudn't Array.prototype.all satisfy those use cases, they run until a falsy value is returned.

# T.J. Crowder (7 years ago)

Is there an Array.prototype.all proposal? I'm not seeing it on github. If you're thinking of every and some, they don't return a reference to the array, which seemed to be important to Naveen for chaining.

# Naveen Chawla (7 years ago)

Does anybody have any opinion on a new Array.prototype.each method that does the same as forEach but returns the array, thereby allowing chaining with sort, map, filter etc., while also preserving backwards compatibility?

# Tab Atkins Jr. (7 years ago)

On Sun, Jul 23, 2017 at 6:53 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

Does anybody have any opinion on a new Array.prototype.each method that does the same as forEach but returns the array, thereby allowing chaining with sort, map, filter etc., while also preserving backwards compatibility?

In general, functions returning undefined are often safe to switch to returning a more useful value; this is one of the recommended patterns for upgrading DOM code that uses callbacks to using promises instead. We should pursue that first. ^_^

# T.J. Crowder (7 years ago)

On Mon, Jul 24, 2017 at 9:58 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

In general, functions returning undefined are often safe to switch to returning a more useful value

I think we'd struggle to prove it could be done safely. For instance, we'd have to differentiate between all the places that did something like this:

someDataPromise().then(data => data.forEach(entry => console.log(entry)));

...that do and do not rely on the fact that promise resolves with undefined. (The above does not.)

Hopefully, ones that do are few and far between. But webscale is massive, "few and far between" can be an unacceptably high number.

If there were an appetite for Array.prototype.each, I'd like to address not just the return value but other issues with Array.prototype.forEach as well (like step value, per-iteration updates of the index, stopping early [using some or every is sometimes perfect, other times semantically misleading]). I have some thoughts on doing that while retaining runtime efficiency for the common case and code simplicity. But I doubt the appetite is there.

-- T.J. (er, the other one) Crowder

# Tab Atkins Jr. (7 years ago)

On Mon, Jul 24, 2017 at 3:31 PM, T.J. Crowder <tj.crowder at farsightsoftware.com> wrote:

On Mon, Jul 24, 2017 at 9:58 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

In general, functions returning undefined are often safe to switch to returning a more useful value

I think we'd struggle to prove it could be done safely. For instance, we'd have to differentiate between all the places that did something like this:

someDataPromise().then(data => data.forEach(entry => console.log(entry)));

...that do and do not rely on the fact that promise resolves with undefined. (The above does not.)

Hopefully, ones that do are few and far between. But webscale is massive, "few and far between" can be an unacceptably high number.

If there were an appetite for Array.prototype.each, I'd like to address not just the return value but other issues with Array.prototype.forEach as well (like step value, per-iteration updates of the index, stopping early [using some or every is sometimes perfect, other times semantically misleading]). I have some thoughts on doing that while retaining runtime efficiency for the common case and code simplicity. But I doubt the appetite is there.

Yeah, thus the "often". ^_^ It's been done a few times in DOM land iirc. Relying on a promise to resolve to undefined is probably quite rare; it would mean that you're explicitly testing for undefined on the far end. Much more likely, in situations like that, is just ignoring the resolution value, in which case switching it to an Array is fine.

# Naveen Chawla (7 years ago)

I've quite frequently required an each that returns the array, since I do sequential transformations that involve forEach, map, filter, sort etc.

I would not overload each with any early termination, I would probably have an Array.prototype.takeWhile method for that (which returns a new array taking elements while the return value of the predicate is truthy).

# T.J. Crowder (7 years ago)

On Tue, Jul 25, 2017 at 5:06 AM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

I would not overload each with any early termination, I would probably have an Array.prototype.takeWhile method for that (which returns a new array taking elements while the return value of the predicate is truthy).

This is for when you don't want to create a new array, for either semantic or memory pressure reasons.

-- T.J. Crowder

# T.J. Crowder (7 years ago)

On Mon, Jul 24, 2017 at 11:39 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

Yeah, thus the "often". ^_^

:-)

It's been done a few times in DOM land iirc.

Nothing's coming to mind, but I haven't watched DOM as closely as JS. It'd be interesting to know what was done, when.

Relying on a promise to resolve to undefined is probably quite rare; it would mean that you're explicitly testing for undefined on the far end.

Not necessarily, you could be testing for any falsy value, etc. Yes, it's rare, but it's not limited to Promises either. People seem obsessed with avoiding having multiple statements if they can get away with one, even with code that makes no semantic sense; I've seen this kind of return from methods many times:

if (someCondition) return somethingKnownNotToHaveAReturnValue();

...where somethingKnownNotToHaveAReturnValue is frequently console.log or alert or even someArray.forEach. Changing the return value of forEach would change the return value of code doing this with forEach. (I confess a small part of me wants to break that code. But it's a small part, and I'm not proud of it. :-) )

Concise arrow functions have made it even more common by making it more hidden; the promise then handler was meant just as an example of a concise arrow function doing this.

For my part, I think forEach is too deeply entrenched with its current return value to get a new one. But "thinking" is just that; proper research would need to be done.

-- T.J. Crowder

# Naveen Chawla (7 years ago)

On Tue, 25 Jul 2017 at 12:09 T.J. Crowder <tj.crowder at farsightsoftware.com>

wrote:

On Tue, Jul 25, 2017 at 5:06 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:

I would not overload each with any early termination, I would probably have an Array.prototype.takeWhile method for that (which returns a new array taking elements while the return value of the predicate is truthy).

This is for when you don't want to create a new array, for either semantic or memory pressure reasons.

-- T.J. Crowder

For that I would have eachWhile which returns the original array but only iterates while the return value of the supplied callback is truthy. The difference with takeWhile (which should be added to ES regardless, along with skipWhile) is that in chaining calls takeWhile provides a potentially smaller array for further processing, whereas eachWhile doesn't. (eachSkipWhile could be the "skipWhile" counterpart to eachWhile).

I would leave each as a contract: always iterates through every value, always returns the original array.

For me this is better for readability.

If these chained calls map, each etc. can be integrated with async iterators (as RxJs attempts with Observables) then some really elegant stuff can happen!

# . 田生 (7 years ago)

i had seen some people transform{  doSth();  return;}toreturn void doSth();for saving the {} and line breaks. So maybereturn arr.forEach(...)is just been written due to same reason.

_____________________________

From: T.J. Crowder <tj.crowder at farsightsoftware.com>

Sent: 星期二, 七月 25, 2017 15:21 Subject: Re: Return value of forEach To: Tab Atkins Jr. <jackalmage at gmail.com>

Cc: <es-discuss at mozilla.org>

On Mon, Jul 24, 2017 at 11:39 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

Yeah, thus the "often". ^_^

:-)

It's been done a few times in DOM land iirc.

Nothing's coming to mind, but I haven't watched DOM as closely as JS. It'd be interesting to know what was done, when.

Relying on a promise to resolve to undefined is probably quite rare; it would mean that you're explicitly testing for undefined on the far end.

Not necessarily, you could be testing for any falsy value, etc. Yes, it's rare, but it's not limited to Promises either. People seem obsessed with avoiding having multiple statements if they can get away with one, even with code that makes no semantic sense; I've seen this kind of return from methods many times:

if (someCondition) return somethingKnownNotToHaveAReturnValue();

...where somethingKnownNotToHaveAReturnValue is frequently console.log or alert or even someArray.forEach. Changing the return value of forEach would change the return value of code doing this with forEach. (I confess a small part of me wants to break that code. But it's a small part, and I'm not proud of it. :-) )

Concise arrow functions have made it even more common by making it more hidden; the promise then handler was meant just as an example of a concise arrow function doing this.

For my part, I think forEach is too deeply entrenched with its current return value to get a new one. But "thinking" is just that; proper research would need to be done.

-- T.J. Crowder