0

In coffescript I have

arr = ["a","b","c"]
for i in [0..arr.length] by 1
  if (sometimesTrue)
    arr.pop()
    i--

But it is translating it to this:

var arr, i, _i, _ref;

arr = ["a", "b", "c"];

for (i = _i = 0, _ref = arr.length; _i <= _ref; i = _i += 1) {
  if (sometimesTrue) {
    arr.pop();
    i--;
  }
}

You can see that this loop uses a _i as the reference rather than i so my i-- doesn't really do anything.

Since in this loop, the length of the array changes, I sort of need to figure out how to handle this... Is there any way to do this with a for loop? Or do I need to switch to a while?

K2xL
  • 9,730
  • 18
  • 64
  • 101
  • 1
    What does `sometimesTrue` really look like? Modifying an array while iterating over it is generally a bad idea (even if you account for the change), producing a copy while filtering tends to be less error prone. And are you sure you want to alter `i` rather than `_ref`? – mu is too short Nov 14 '12 at 22:49
  • It's part of an algorithm I'm writing - yes I could do it in that way, but I imagined there would be away to access the iterator used in the loop in coffeescript.. guess there isn't – K2xL Nov 15 '12 at 02:46
  • 1
    You can read the loop counter (`for e, i in ary`) but you can't change it. – mu is too short Nov 15 '12 at 03:42

4 Answers4

2

CoffeeScript will compute the loop bounds once and you can't reset the calculation so changing the array while you're iterating over it will just make a big mess.

For example, this:

f(i) for i in [0..a.length]

becomes this:

var i, _i, _ref;
for (i = _i = 0, _ref = a.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
  f(i);
}

Note that the number of iterations is fixed when _ref is computed at the beginning of the loop. Also note that i will be assigned a new value on each iteration so any changes you make to i inside the loop will be ignored. And, note that looping over [0..a.length] does a.length+1 iterations, not a.length iterations; [a..b] produces a closed interval (i.e. contains both end points), [a...b] gives you a half-open interval (i.e. b is not included). Similarly, this:

f(i) for i in a

becomes this:

var i, _i, _len;
for (_i = 0, _len = a.length; _i < _len; _i++) {
  i = a[_i];
  f(i);
}

Again, the number of iterations is fixed and changes to i are overwritten.

If you want to mess around the the array and the loop index inside the loop then you have to do it all by hand using a while loop:

i = 0
while i < arr.length
  if(sometimesTrue)
    arr.pop()
    --i
  ++i

or:

i = 0
while i < arr.length
  if(sometimesTrue)
    arr.pop()
  else
    ++i
mu is too short
  • 426,620
  • 70
  • 833
  • 800
1

Modifying the array you're looping over rarely does what you want in languages with for ... in ... constructs. What you're really looking for is a filter. Many javascript implementations have a filter function attached to the array prototype:

arr = arr.filter((member) -> !sometimesTrue)

If you can't count on this, you can use a similar CoffeeScript construct:

arr = (member for member in arr when !sometimesTrue)
Aaron Dufour
  • 17,288
  • 1
  • 47
  • 69
-1

Using indices in coffeescript is quite unnatural.

I think that what you want is something like :

arr = ["a","b","c"]
arr = (i for i in arr when ! sometimeTrue )

I think that you should read the following topic "Loops and Comprehensions" at http://coffeescript.org/

VGE
  • 4,171
  • 18
  • 17
-2

Sometimes it helps to take a walk, maybe in real fresh air in the woods, maybe in front of the house with a cigarrette (though your wife might hate the smoking) ... Somtimes it even helps to talk to the rubber duck some uber-geeks have on their monitors. And sometimes it plain and simple helps to attack the simple things from a different angle. arr = ["a", "b", "c"]

for i in [0..arr.length]
  if (sometimesTrue)
    arr.splice i, 1
  else i++

EDIT: As seen in the comments below I was missing something here. I is indeed not ignored at all, how could I've thought that in the first place?

Joehannes
  • 301
  • 2
  • 9
  • ahmm ... I just thought you might need the splice, not the pop – Joehannes Nov 15 '12 at 02:02
  • have you seen the javascript this outputs? (js2coffe.org) not pretty. – K2xL Nov 15 '12 at 02:43
  • hmm, can't exactly see your point matey! the issue here wasn't being pretty but functional I thought. Besides, I don't think the js is that ugly by any means, but then I use coffeescript a lot and get used to its compilated stuff. Just for your info: coffeescript.org has a live-compiler under "try coffeescript" as well :) To make the point more clear: I recognize the question as: "I want to use the i in an array I'm manipulating in the for-loop - hence i isn't incremented by the loop since _i is introduced, I cannot use it properly" ... Therefore the else i++ – Joehannes Nov 15 '12 at 03:08
  • just to make sure you are satisfied with the beauty of the translation @K2xL ... I would suggest one could actually leave off the range stuff and just say for i in arr since coffee introduces a puffer var _len on initialization and therefore in-the-act-arrayManipulation is - though a bit dangerous - not harmful ... But then, I cannot say this came to me automatically - but only on second thought – Joehannes Nov 15 '12 at 03:11
  • But `i` is readonly-ish inside the loop so changing `i` inside the loop is pointless. – mu is too short Nov 15 '12 at 03:41
  • And the number of iterations is fixed when the loop starts so you'll run off the end of the array if you shorten `arr` inside the loop. – mu is too short Nov 15 '12 at 04:33
  • @mu is too short ... I'm afraid you missed the initial declaration in the original question, that coffee is actually creating it's own iteration var _i ... i is like a normal var you __can__ change that's never touched (incremented by the loop) ... so as to simulated the effect of reducing it by i--, I'm just flipping it round, increasing it from the 0 it would always be to i++, just like it would have been done if it was the iteration var -- but then not in those cases it would have been decremented --- so I guess that's a valid answer – Joehannes Nov 15 '12 at 13:41
  • Um, no. You need to look at the JavaScript: `for (i = _i = 0, _ref = arr.length; _i <= _ref; i = _i += 1)`. `i` is overwritten on each iteration so changing `i` in the loop body does nothing and the number of iterations (`_ref`) is fixed when the loop starts so shortening the array means that you'll run off the end. Manipulating `i` inside the loop body is pointless. Perhaps you're using an older version of CoffeeScript that directly manipulates `i` in the loop; compare your loop at coffeescript.org and jsfiddle.net and you'll see what's going on. – mu is too short Nov 15 '12 at 18:18
  • well, that's interesting ... when I go to coffeescript and translate the given for-comprehension-loop it gives me for (i = _i = 0, _ref = arr.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { ... wow, ok, I see my err now, indeed. apologies – Joehannes Nov 15 '12 at 22:03
  • I think it depends on the CoffeeScript version. CoffeeScript.org has the latest and an [unwriteable `i`](http://coffeescript.org/#try:i%20for%20i%20in%20%5B0..10%5D), jsfiddle.net seems to use an older version with a [writeable `i`](http://jsfiddle.net/ambiguous/jeVje/) (hit *Show JS* in the toolbar). – mu is too short Nov 17 '12 at 07:46