Return value of forEach
const a = [1, 2, 3]
.map(square)
.map(x => console.log(x) || x )
.map(plus1)
.reduce(add);
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.
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 >.<
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?
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.
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.
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
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)
I like the idea, I don't see why it might break compatibility and this is also faster than map if list is immutable.
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?
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 tap
ping into the pipeline? I would love to
see this make it in. It's very easily optimizable.
You mean to say you don't have
var undefined = [].forEach(Array.prototype.forEach.call);
at the top of every file!?
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
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.
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20170706/618947df/attachment
Woudn't Array.prototype.all satisfy those use cases, they run until a falsy value is returned.
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.
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?
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. ^_^
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
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 withArray.prototype.forEach
as well (like step value, per-iteration updates of the index, stopping early [usingsome
orevery
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.
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).
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 anArray.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
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
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 anArray.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!
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
Currently,
Array.prototype.forEach
returnsundefined
. 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
returnsundefined
. I now have to comment out all the code below it. Having the plug and play behaviour forforEach
would be very convenient.