265

The typical way to loop x times in JavaScript is:

for (var i = 0; i < x; i++) doStuff(i); 

But I don't want to use the ++ operator or have any mutable variables at all. So is there a way, in ES6, to loop x times another way? I love Ruby's mechanism:

x.times do |i| do_stuff(i) end 

Anything similar in JavaScript/ES6? I could kind of cheat and make my own generator:

function* times(x) { for (var i = 0; i < x; i++) yield i; } for (var i of times(5)) { console.log(i); } 

Of course I'm still using i++. At least it's out of sight :), but I'm hoping there's a better mechanism in ES6.

7
  • 4
    Why is the mutable loop control variable an issue? Just a principle? Commented May 26, 2015 at 7:33
  • 3
    @doldt - I'm trying to teach JavaScript, but I'm experimenting with delaying the concept of mutable variables until later Commented May 26, 2015 at 7:37
  • 8
    We're getting really off-topic here, but are you sure that moving on to ES6 generators (or any other new, high level concept) is a good idea before they learn about mutable variables?:) Commented May 26, 2015 at 7:38
  • 8
    @doldt - maybe, I'm experimenting. Taking a functional language approach to JavaScript. Commented May 26, 2015 at 7:43
  • 1
    Use let to declare that variable in the loop. Its scope ends with the loop. Commented Jul 23, 2020 at 18:35

24 Answers 24

425

Using the ES2015 Spread operator:

[...Array(n)].map()

const res = [...Array(10)].map((_, i) => { return i * 10; }); // as a one liner const res = [...Array(10)].map((_, i) => i * 10); 

Or if you don't need the result:

[...Array(10)].forEach((_, i) => { console.log(i); }); // as a one liner [...Array(10)].forEach((_, i) => console.log(i)); 

Or using the ES2015 Array.from operator:

Array.from(...)

const res = Array.from(Array(10)).map((_, i) => { return i * 10; }); // as a one liner const res = Array.from(Array(10)).map((_, i) => i * 10); 

Note that if you just need a string repeated you can use String.prototype.repeat.

console.log("0".repeat(10)) // 0000000000 
Sign up to request clarification or add additional context in comments.

11 Comments

Better: Array.from(Array(10), (_, i) => i*10)
If you don't need the iterator (i), you can exclude both the key and value to make this: [...Array(10)].forEach(() => console.log('looping 10 times');
So you allocate entire array of N elements just to throw it away?
Has anyone addressed the previous comment by Kugel? I was wondering the same thing
@sebpiq Because the Array(10) function returns an empty array instance with length set to 10. The array instance is essentially allocated in memory but empty. If you try to map() over it, it will fail because the array is empty. When you try to spread it, though, the spread operator will return the same number of items as the array's length. Since the array is empty, those items are undefined (non-existent), so spread will give you 10 elements === undefined. Hence the (_, i) => {} syntax to always ignore the first (constantly undefined) parameter.
|
189

OK!

The code below is written using ES6 syntaxes but could just as easily be written in ES5 or even less. ES6 is not a requirement to create a "mechanism to loop x times"


If you don't need the iterator in the callback, this is the most simple implementation

const times = x => f => { if (x > 0) { f() times (x - 1) (f) } } // use it times (3) (() => console.log('hi')) // or define intermediate functions for reuse let twice = times (2) // twice the power ! twice (() => console.log('double vision'))

If you do need the iterator, you can use a named inner function with a counter parameter to iterate for you

const times = n => f => { let iter = i => { if (i === n) return f (i) iter (i + 1) } return iter (0) } times (3) (i => console.log(i, 'hi'))


Stop reading here if you don't like learning more things ...

But something should feel off about those...

  • single branch if statements are ugly — what happens on the other branch ?
  • multiple statements/expressions in the function bodies — are procedure concerns being mixed ?
  • implicitly returned undefined — indication of impure, side-effecting function

"Isn't there a better way ?"

There is. Let's first revisit our initial implementation

// times :: Int -> (void -> void) -> void const times = x => f => { if (x > 0) { f() // has to be side-effecting function times (x - 1) (f) } }

Sure, it's simple, but notice how we just call f() and don't do anything with it. This really limits the type of function we can repeat multiple times. Even if we have the iterator available, f(i) isn't much more versatile.

What if we start with a better kind of function repetition procedure ? Maybe something that makes better use of input and output.

Generic function repetition

// repeat :: forall a. Int -> (a -> a) -> a -> a const repeat = n => f => x => { if (n > 0) return repeat (n - 1) (f) (f (x)) else return x } // power :: Int -> Int -> Int const power = base => exp => { // repeat <exp> times, <base> * <x>, starting with 1 return repeat (exp) (x => base * x) (1) } console.log(power (2) (8)) // => 256

Above, we defined a generic repeat function which takes an additional input which is used to start the repeated application of a single function.

// repeat 3 times, the function f, starting with x ... var result = repeat (3) (f) (x) // is the same as ... var result = f(f(f(x))) 

Implementing times with repeat

Well this is easy now; almost all of the work is already done.

// repeat :: forall a. Int -> (a -> a) -> a -> a const repeat = n => f => x => { if (n > 0) return repeat (n - 1) (f) (f (x)) else return x } // times :: Int -> (Int -> Int) -> Int const times = n=> f=> repeat (n) (i => (f(i), i + 1)) (0) // use it times (3) (i => console.log(i, 'hi'))

Since our function takes i as an input and returns i + 1, this effectively works as our iterator which we pass to f each time.

We've fixed our bullet list of issues too

  • No more ugly single branch if statements
  • Single-expression bodies indicate nicely separated concerns
  • No more useless, implicitly returned undefined

JavaScript comma operator, the

In case you're having trouble seeing how the last example is working, it depends on your awareness of one of JavaScript's oldest battle axes; the comma operator – in short, it evaluates expressions from left to right and returns the value of the last evaluated expression

(expr1 :: a, expr2 :: b, expr3 :: c) :: c 

In our above example, I'm using

(i => (f(i), i + 1)) 

which is just a succinct way of writing

(i => { f(i); return i + 1 }) 

Tail Call Optimisation

As sexy as the recursive implementations are, at this point it would be irresponsible for me to recommend them given that no JavaScript VM I can think of supports proper tail call elimination – babel used to transpile it, but it's been in "broken; will reimplement" status for well over a year.

repeat (1e6) (someFunc) (x) // => RangeError: Maximum call stack size exceeded 

As such, we should revisit our implementation of repeat to make it stack-safe.

The code below does use mutable variables n and x but note that all mutations are localized to the repeat function – no state changes (mutations) are visible from outside of the function

// repeat :: Int -> (a -> a) -> (a -> a) const repeat = n => f => x => { let m = 0, acc = x while (m < n) (m = m + 1, acc = f (acc)) return acc } // inc :: Int -> Int const inc = x => x + 1 console.log (repeat (1e8) (inc) (0)) // 100000000

This is going to have a lot of you saying "but that's not functional !" – I know, just relax. We can implement a Clojure-style loop/recur interface for constant-space looping using pure expressions; none of that while stuff.

Here we abstract while away with our loop function – it looks for a special recur type to keep the loop running. When a non-recur type is encountered, the loop is finished and the result of the computation is returned

const recur = (...args) => ({ type: recur, args }) const loop = f => { let acc = f () while (acc.type === recur) acc = f (...acc.args) return acc } const repeat = $n => f => x => loop ((n = $n, acc = x) => n === 0 ? acc : recur (n - 1, f (acc))) const inc = x => x + 1 const fibonacci = $n => loop ((n = $n, a = 0, b = 1) => n === 0 ? a : recur (n - 1, b, a + b)) console.log (repeat (1e7) (inc) (0)) // 10000000 console.log (fibonacci (100)) // 354224848179262000000

9 Comments

Seems overcomplicated (I'm especially confused with g => g(g)(x)). Is there a benefit from a higher-order function over a first-order one, like in my solution?
@AlfonsoPérez I appreciate the remark. I'll see if I can work a little hint in there somewhere ^_^
@naomik Farewell TCO! I am devastated.
It seems this answer is accepted and well-rated because it must have taken a lot of effort, but I don't think it's a good answer. The correct answer to the question is "no". It is helpful to list a workaround as you did, but right after that you state that there is a better way. Why don't you just put that answer and remove the worse one at the top? Why are you explaining comma operators? Why do you bring up Clojure? Why, in general, so many tangents for a question with a 2 character answer? Simple questions aren't just a platform for users to do a presentation on some neat programming facts.
@Timofey This answer is the compilation of several edits over the course of 2 years. I agree that this answer does need some final editing, but your edits removed too much. I'll revisit it soon, taking your comment and edit suggestions into sincere consideration.
|
50

Here is another good alternative:

Array.from({ length: 3}).map(...); 

Preferably, as @Dave Morse pointed out in the comments, you can also get rid of the map call, by using the second parameter of the Array.from function like so:

Array.from({ length: 3 }, () => (...)) 

2 Comments

This should be the accepted answer! One small suggestion - you already get the map-like functionality you need for free with Array.from: Array.from({ length: label.length }, (_, i) => (...)) This saves creating an empty temporary array just to kick off a call to map.
48
for (let i of Array(100).keys()) { console.log(i) } 

12 Comments

This works, so that's great! But is a bit ugly in the sense that extra work is needed and this isn't what Array keys are used for.
@at. indeed. But I'm not sure there is a haskell's synonym for [0..x] in JS more concise than in my answer.
you might be correct that there's nothing more concise than this.
OK, I understand why this works given the differences between Array.prototype.keys and Object.prototype.keys, but it sure is confusing at first glance.
@cchamberlain with TCO in ES2015 (not implemented anywhere though?) it might be the less of concern, but indeed :-)
|
41

I think the best solution is to use let:

for (let i=0; i<100; i++) … 

That will create a new (mutable) i variable for each body evaluation and assures that the i is only changed in the increment expression in that loop syntax, not from anywhere else.

I could kind of cheat and make my own generator. At least i++ is out of sight :)

That should be enough, imo. Even in pure languages, all operations (or at least, their interpreters) are built from primitives that use mutation. As long as it is properly scoped, I cannot see what is wrong with that.

You should be fine with

function* times(n) { for (let i = 0; i < n; i++) yield i; } for (const i of times(5)) { console.log(i); } 

But I don't want to use the ++ operator or have any mutable variables at all.

Then your only choice is to use recursion. You can define that generator function without a mutable i as well:

function* range(i, n) { if (i >= n) return; yield i; return yield* range(i+1, n); } times = (n) => range(0, n); 

But that seems overkill to me and might have performance problems (as tail call elimination is not available for return yield*).

3 Comments

This is simple and to the point and does not allocate an array like many answers above
@Kugel The second one might allocate on the stack, though
Good point not sure if tail-call optimisation will work here @Bergi
18

I think it is pretty simple:

[...Array(3).keys()] 

or

Array(3).fill() 

Comments

17
const times = 4; new Array(times).fill().map(() => console.log('test')); 

This snippet will console.log test 4 times.

2 Comments

Whats the support for fill ?
@AamirAfridi You can check the Browser compatibility section, there is also a polyfill provided: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
14

Answer: 09 December 2015

Personally, I found the accepted answer both concise (good) and terse (bad). Appreciate this statement might be subjective, so please read this answer and see if you agree or disagree

The example given in the question was something like Ruby's:

x.times do |i| do_stuff(i) end 

Expressing this in JS using below would permit:

times(x)(doStuff(i)); 

Here is the code:

let times = (n) => { return (f) => { Array(n).fill().map((_, i) => f(i)); }; }; 

That's it!

Simple example usage:

let cheer = () => console.log('Hip hip hooray!'); times(3)(cheer); //Hip hip hooray! //Hip hip hooray! //Hip hip hooray! 

Alternatively, following the examples of the accepted answer:

let doStuff = (i) => console.log(i, ' hi'), once = times(1), twice = times(2), thrice = times(3); once(doStuff); //0 ' hi' twice(doStuff); //0 ' hi' //1 ' hi' thrice(doStuff); //0 ' hi' //1 ' hi' //2 ' hi' 

Side note - Defining a range function

A similar / related question, that uses fundamentally very similar code constructs, might be is there a convenient Range function in (core) JavaScript, something similar to underscore's range function.

Create an array with n numbers, starting from x

Underscore

_.range(x, x + n) 

ES2015

Couple of alternatives:

Array(n).fill().map((_, i) => x + i) Array.from(Array(n), (_, i) => x + i) 

Demo using n = 10, x = 1:

> Array(10).fill().map((_, i) => i + 1) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] > Array.from(Array(10), (_, i) => i + 1) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 

In a quick test I ran, with each of the above running a million times each using our solution and doStuff function, the former approach (Array(n).fill()) proved slightly faster.

Comments

14

I am late to the party, but since this question turns up often in search results, I would just like to add a solution that I consider to be the best in terms of readability while not being long (which is ideal for any codebase IMO). It mutates, but I'd make that tradeoff for KISS principles.

let times = 5 while( times-- ) console.log(times) // logs 4, 3, 2, 1, 0 

4 Comments

Thank you for being the voice of reason in what I can only describe as a higher-order lambda fetish party. I too ended up on this Q&A following an innocuous first-hit-on-Google-path and swiftly had my sanity desecrated by most of the answers here. Yours is the first one in the list that I would consider a straightforward solution to a straightforward problem.
Only issue with this is it's a little counterintuitive if you want to use the times variable inside the loop. Perhaps countdown would be a better naming. Otherwise, cleanest and clearest answer on the page.
This is acceptable answer only when the index value is not required, otherwise looping down might not be acceptable in certain scenarios.
This still has a mutable variable.
9
Array(100).fill().map((_,i)=> console.log(i) ); 

This version satisifies the OP's requirement for immutability. Also consider using reduce instead of map depending on your use case.

This is also an option if you don't mind a little mutation in your prototype.

Number.prototype.times = function(f) { return Array(this.valueOf()).fill().map((_,i)=>f(i)); }; 

Now we can do this

((3).times(i=>console.log(i))); 

+1 to arcseldon for the .fill suggestion.

1 Comment

Voted down, as fill method is not supported in IE or Opera or PhantomJS
7

Not something I would teach (or ever use in my code), but here's a codegolf-worthy solution without mutating a variable, no need for ES6:

Array.apply(null, {length: 10}).forEach(function(_, i){ doStuff(i); }) 

More of an interesting proof-of-concept thing than a useful answer, really.

3 Comments

Coudn't Array.apply(null, {length: 10}) be just Array(10)?
@Pavlo, actually, no. Array(10) would create an array of length 10, but without any keys defined in it, which makes the forEach construct not usable in this case. But indeed it can be simplified if you dont use forEach, see zerkms 's answer (that uses ES6 though!).
creative @doldt, but I'm looking for something teachable and simple.
4

If you're willing to use a library, there's also lodash _.times or underscore _.times:

_.times(x, i => { return doStuff(i) }) 

Note this returns an array of the results, so it's really more like this ruby:

x.times.map { |i| doStuff(i) } 

Comments

3

Afaik, there is no mechanism in ES6 similar to Ruby's times method. But you can avoid mutation by using recursion:

let times = (i, cb, l = i) => { if (i === 0) return; cb(l - i); times(i - 1, cb, l); } times(5, i => doStuff(i)); 

Demo: http://jsbin.com/koyecovano/1/edit?js,console

Comments

3

In the functional paradigm repeat is usually an infinite recursive function. To use it we need either lazy evaluation or continuation passing style.

Lazy evaluated function repetition

const repeat = f => x => [x, () => repeat(f) (f(x))]; const take = n => ([x, f]) => n === 0 ? x : take(n - 1) (f()); console.log( take(8) (repeat(x => x * 2) (1)) // 256 );

I use a thunk (a function without arguments) to achieve lazy evaluation in Javascript.

Function repetition with continuation passing style

const repeat = f => x => [x, k => k(repeat(f) (f(x)))]; const take = n => ([x, k]) => n === 0 ? x : k(take(n - 1)); console.log( take(8) (repeat(x => x * 2) (1)) // 256 );

CPS is a little scary at first. However, it always follows the same pattern: The last argument is the continuation (a function), which invokes its own body: k => k(...). Please note that CPS turns the application inside out, i.e. take(8) (repeat...) becomes k(take(8)) (...) where k is the partially applied repeat.

Conclusion

By separating the repetition (repeat) from the termination condition (take) we gain flexibility - separation of concerns up to its bitter end :D

Comments

1

Advantages of this solution

  • Simplest to read / use (imo)
  • Return value can be used as a sum, or just ignored
  • Plain es6 version, also link to TypeScript version of the code

Disadvantages - Mutation. Being internal only I don't care, maybe some others will not either.

Examples and Code

times(5, 3) // 15 (3+3+3+3+3) times(5, (i) => Math.pow(2,i) ) // 31 (1+2+4+8+16) times(5, '<br/>') // <br/><br/><br/><br/><br/> times(3, (i, count) => { // name[0], name[1], name[2] let n = 'name[' + i + ']' if (i < count-1) n += ', ' return n }) function times(count, callbackOrScalar) { let type = typeof callbackOrScalar let sum if (type === 'number') sum = 0 else if (type === 'string') sum = '' for (let j = 0; j < count; j++) { if (type === 'function') { const callback = callbackOrScalar const result = callback(j, count) if (typeof result === 'number' || typeof result === 'string') sum = sum === undefined ? result : sum + result } else if (type === 'number' || type === 'string') { const scalar = callbackOrScalar sum = sum === undefined ? scalar : sum + scalar } } return sum } 

TypeScipt version
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011

Comments

1

I am just going to put this here. If you are looking for a compact function without using Arrays and you have no issue with mutability/immutability :

var g =x=>{/*your code goes here*/x-1>0?g(x-1):null}; 

Comments

1

The simplest way I can think of for creating list/array within range

Array.from(Array(max-min+1), (_, index) => index+min)

Comments

1

It seems to me that the most correct answer (which is debatable) to this question is buried in a comment by Sasha Kondrashov and is also the most concise, using just two characters: "no". There is no functional alternative to a for-loop as nice as the syntax that Ruby has. We might want there to be one, but there just isn't.

It is not explicitly stated in the question, but I would argue any solution to the problem of 'looping N times' should not allocate memory, at least not proportional to N. That criterium would rule out most of the answers that are 'native to javascript'.

Other answers show implementations like the one in Ruby, which is fine, except that the question explicitly asks for a native javascript solution. And there is already a very decent hand-rolled solution in the question, arguably one of the most readable of all.

Comments

1

I have another alternative

[...Array(30).keys()] 

1 Comment

0

addressing the functional aspect:

function times(n, f) { var _f = function (f) { var i; for (i = 0; i < n; i++) { f(i); } }; return typeof f === 'function' && _f(f) || _f; } times(6)(function (v) { console.log('in parts: ' + v); }); times(6, function (v) { console.log('complete: ' + v); }); 

5 Comments

"addressing the functional aspect" and then using imperative loop with a mutable i. What is the reason to even use times over plain old for then?
reuse like var twice = times(2);.
So why not just use for twice?
i am not afraid to use for. the question was something not to use a variabele. but the result is always some kind of caching aka variable.
"was something not to use a variabele" --- and you still use it - i++. It's not obvious how wrapping something unacceptable in a function makes it better.
0

I wrapped @Tieme s answer with a helper function.

In TypeScript:

export const mapN = <T = any[]>(count: number, fn: (...args: any[]) => T): T[] => [...Array(count)].map((_, i) => fn()) 

Now you can run:

const arr: string[] = mapN(3, () => 'something') // returns ['something', 'something', 'something'] 

Comments

0

I made this:

function repeat(func, times) { for (var i=0; i<times; i++) { func(i); } } 

Usage:

repeat(function(i) { console.log("Hello, World! - "+i); }, 5) /* Returns: Hello, World! - 0 Hello, World! - 1 Hello, World! - 2 Hello, World! - 3 Hello, World! - 4 */ 

The i variable returns the amount of times it has looped - useful if you need to preload an x amount of images.

Comments

0

For me, this is the easiest answer to understand for many levels of developers

const times = (n, callback) => { while (n) { callback(); n--; } } times(10, ()=> console.log('hello'))

1 Comment

Is there a way to get the value of n within the times function?
-2

Generators? Recursion? Why so much hatin' on mutatin'? ;-)

If it is acceptable as long as we "hide" it, then just accept the use of a unary operator and we can keep things simple:

Number.prototype.times = function(f) { let n=0 ; while(this.valueOf() > n) f(n++) } 

Just like in ruby:

> (3).times(console.log) 0 1 2 

4 Comments

Thumbs up for simplicity, thumbs down for going ruby-style a bit too much with the monkeypatch. Just say no to those bad bad monkeys.
@mrm is this "monkey patching", isn't this just a case of extension? Embrace & extend :)
No. Adding functions to Number (or String or Array or any other class that you didn't author) are, by definition, either polyfills or monkey patches--and even polyfills are not recommended. Read the definitions of "monkey patch", "polyfill," and a recommended alternative, "ponyfill." That's what you want.
To extend Number you would do: class SuperNumber extends Number { times(fn) { for (let i = 0; i < this; i ++) { fn(i); } } }

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.