let button = document.getElementsByClassName('button')[0]
Array(100000).fill(undefined).forEach(() => button.click())
In response, I got the following log message: > Such a smart subject. let button = document.getElementsByClassName('button')[0]
Array(100000).fill(undefined).forEach(() => button.click())
In response, I got the following log message: > Such a smart subject.Guessing it's bc one-liners are easier to write than for loops in the console.
A for loop can implement a reduce, a reduce can implement a map, a map can implement a forEach, but not the inverse.
In [1]: def lred(function, iterable, initializer):
...: acc = initializer
...: def reductor(el):
...: nonlocal acc
...: acc = function(acc, el)
...: for n in map(reductor, iterable): pass
...: return acc
...:
In [2]: lred(lambda a,b: 10*a + b, [2,4,6,3,1], 40)
Out[2]: 4024631
What's stopping you from running these equivalencies in either direction?A map can run code per list element but the result must be injective (one-to-one).
Reduce can do all of the above, but must return a lower dimension result from its input.
And finally, a for loop is basically omnipotent.
What do you mean by this? You can accumulate just about anything into the resulting object, including a copy of the original array.
Of course, in fact you could hack anything in any of these, since you could be doing other work in the function called. But I think the general principle is sound.
Don't tell Common Lisp.
So can map. Ditto.
So can foreach. Ditto again.
for el in values:
if el == 3: pass
else:
do_something_with( el ) for el in values:
if el == 3: return
do_something_with( el )
For the other two, you still can, but you'll need to handle the control flow yourself, by doing something like goto or invoking a continuation.I take it you've never read any Python? That's a forEach. Python doesn't even have a for loop construct (though it does have while, which is equivalent).
> And depending on the language I'm pretty sure that return call would exit the parent scope.
Yes, that's the point, that's an illustration of terminating the forEach before you've processed each element.
If you have a C-style for-loop, you can and have to do everything yourself. That means you can skip elements or processer them twice, etc.
In a Python-style for-each loop, you can break out with break or return, but you have a harder time skipping or changing the order of processing. So they are weaker. And that's good.
(Your examples still process elements in the body of the loop. It's just that sometimes the body decides to do a no-op.)
Putting the no-op logic in the body of the loop, or in the function you pass to reduce is different than being able to short-cut evaluation.
You can see the difference most clearly, when trying to process (the start of) an infinite generator with reduce or a for-loop. Reduce will just hang.
You might notice this problem above, where it's necessary to do
for n in map(reductor, iterable): pass
to realize the mapping. acc = []
for el in values:
acc.append(f(el))
return acc
This hierarchy doesn't exist; you can pretty much implement any of them in terms of the others.To make that untrue, you need to define very strict limits on what else your language can do.
(Sure, we produce a transformed list here through the use of side effects. But note that the original example sparking the claim that forEach can't do what map can was this:
.forEach(() => button.click())
Side effects are clearly allowed.)You can implement any of them in terms of the others, but only if you break convention and introduce side effects. Follow convention and try to implement (for example) reduce with map and you'll find that it's not possible.
This can't be right. forEach has no effects other than side effects. A convention that says not to use side effects prevents you from using forEach at all.
But that would make claims about the place of forEach in a hierarchy into meaningless nonsense.
> The forEach isn't returning it in your example, the line after it is returning.
That depends on your point of view. C has numerous functions which accept pointer parameters and return values in those parameters. That's just the normal way to return multiple values in C.
And by that standard, the forEach itself is returning the transformed list; that's where the transformation occurs. The following `return` line is only necessary if this is a snippet within a function whose purpose is to execute a for statement; you would actually do this inline, by just writing the for statement without needing a following `return`.
And without side-effects, a map can't implement a forEach.
Yes, it can, because without side effects, forEach is a NOP. That's not difficult to implement. As long as you don't do anything with the return value from map, you're there.
Firefox has a pretty good multi-line editor now if anyone was looking for that
Array.from({ length: 10000 }, button.click);
[...new Array(1000)].map(something)
[...Array(1000)]
I think that is the least bits. Would love to be proven wrong :D
That's what I like in the elegance of Array.from({ length: 1000 }): it's a hack, but it reads naturally.
But your approach makes perfect sense and is so much cleaner. Thank you!
If anyone is wondering why it works, it's because everything in JavaScript is an object. So, an array is an object with a length prop. Apparently Array.from just needs any object with a length prop to work
Similarly, when I pick up a feathered pen in real life I become a duck
Well, .duck isn't a TLD yet and it may not be publicly available...
Maybe a contender to CookieClicker? 73% achievements atm
Open a console, and prep: xdotool click --delay 50 --repeat 1000 1
Move your mouse to the location and press enter.
for(i of Array(1000).keys()) { doSomething() }
or [...Array(1000)].forEach(() => doSomething())for(let i = 0; i < 1000; i++) button.click()
wouldn’t be surprised if Javascript has some weird optimization under the hood!
E.g.
// Get list of even squares of first 10 integers
Array.from({ length: 10 }).map((e,i)=>i*i).filter((i)=>i%2==0).forEach((i)=>console.log(i));
// Or more readable
Array.from({ length: 10 })
.map((e,i)=>i*i) // Get squares of each number
.filter((i)=>i%2==0) // filter for evens
.forEach((i)=>console.log(i)); // print
Want to filter or manipulate the values in a different way? Throw in another map or filter. Want to pass complex values between steps? You can pack them into an array or object in a map and unpack later.[1] Once you're used to doing stuff with lists, you can use some other interesting list operations like reduce(), some(), every(), etc.It's not really better (it's subjective), it's just different and a lot of people are used to it and prefer it (and it's fairly consistent in nomenclature across some languages, which is a bonus).
[...Array(n)].map(something)
dotimes(n, () => button.click())
If anyone is in the habit of solving trivial constant space problems in linear space where n is expected to be over 100000, I don't know what to say to them. Are Javascript implementations pretty much guaranteed to optimise the array away?For loops are just clunky. They're not values, you can't pass them around or copy'n'paste them where the transformed value is needed.