42

What is the most concise equivalent Coffeescript to the following:

# ruby    
3.times { puts 'hi' }

?

The best I could think of is:

# coffeescript
for n in [1..3]
  console.log 'hi'
sivabudh
  • 31,807
  • 63
  • 162
  • 228

5 Answers5

62
console.log 'hi' for [1..3]

To also handle 0 correctly:

console.log 'hi' for [1..n] if n

Or with prototype magic:

Number::times = (fn) ->
  do fn for [1..@valueOf()] if @valueOf()
  return
3.times -> console.log 'hi'

Note that the second method isn't recommended because changing the Number prototype has global effects.

Edit: Changed according to @BrianGenisio's comment (.prototype. -> ::)

Edit 2: fixed handling of 0, thanks @Brandon

thejh
  • 44,854
  • 16
  • 96
  • 107
  • I'm torn between picking your answer or @tokland. – sivabudh Nov 04 '11 at 20:21
  • 3
    I picked this answer because it only uses Coffeescript. – sivabudh Nov 04 '11 at 20:23
  • 2
    Note that this prototype magic is added by the [Sugar.js](http://sugarjs.com/) library. Really, there's nothing wrong with using prototype magic, as long as only one part of your application is doing it—so you don't have different things expecting different prototype magics. – Trevor Burnham Nov 04 '11 at 20:54
  • 3
    Just to play with it a bit, I'd suggest using CoffeeScripts `::` operator: `Number::times = (fn) ->` It is the CoffeeScript way :) – Brian Genisio Nov 05 '11 at 00:35
  • 1
    @TrevorBurnham: +1 for mentioning Sugar.js; I have never heard of it before. Looks very useful. Thanks! – sivabudh Nov 05 '11 at 01:07
  • 2
    The range should actually be [0...@valueOf()]. As it is currently written, when @valueOf() equals 1, fn is called zero times, and when @valueOf() equals 0, fn is called one time. – Brandon May 07 '14 at 22:37
  • 1
    @Brandon `[1..1]` becomes `[1]`, so I'd say it's correct for `@valueOf()==1`. But yes, that zero case needs special handling, thanks for pointing that out. Fixing it. – thejh May 08 '14 at 14:19
  • I just want to add that in Ruby `n.times` will yield the iteration to the callback function. In other words: `do (-> fn(i-1)) for i in [1..@valueOf()] if @valueOf()` Maybe someone can do it more elegant. – 2called-chaos Jan 01 '20 at 20:33
33

Since you're already using Underscore.js along with CoffeeScript:

_(3).times -> console.log('hi')
tokland
  • 66,169
  • 13
  • 144
  • 170
8

JavaScript arrays (at least the modern ones) have a forEach method and CoffeeScript [1..3] ranges are arrays so you could do this:

[1..3].forEach -> console.log 'hi'

One warning though: this might be a bit hard on the browser if your n in [1..n] is large as you'll be building a large array just to get a convenient notation; but if n is small then the overhead of building the array shouldn't matter that much.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
1
console.log 'hi' for[]in length:3
matyr
  • 5,774
  • 28
  • 22
  • 10
    *woosh…* don't tell me you actually use that! :) – Ricardo Tomasi Nov 05 '11 at 05:47
  • I don't understand this code. Could you explain how it works? – Michael Dorst Jul 29 '13 at 23:50
  • It works though. It produces the following JavaScript: `_ref = { length: 3 }; for (_j = 0, _len = _ref.length; _j < _len; _j++) { _ref[_j]; console.log('hi'); }` – Saim Jul 29 '20 at 18:48
  • `console.log 'hi' for _ in length:3` is more readable in my opinion. It's very hacky, but it's easy to understand what it does. It also handles `0` correctly without an extra `if`. – Andri Oct 01 '21 at 08:33
0

using lodash:

_.times 3, -> console.log 'hi'
trushkevich
  • 2,657
  • 1
  • 28
  • 37