For Each … Next will skip elements if the array is reduced during the loop

Consider this code:

  dim arr() as integer = array( 1, 2, 3, 4 )
  
  for each i as integer in arr
    arr.Remove arr.IndexOf( i )
  next

My understanding of For Each is that it guarantees that each element of an array will be put through the loop, so I’d expect arr to be empty when this loop is done. Instead, it acts just as if I had iterated over it using for i = 0 to arr.Ubound.

Should this be considered a bug? I’m not sure as the languages I arbitrarily tried this morning handled it differently. Applescript and PHP left me with an empty array while JavaScript worked like Xojo.

Opinions?

Why go through that when you have Redim ?

Besides, as far as I can figure, For Each in Xojo indeed seems to work like For i = 0 to arr.ubound

This is test code to demonstrate the issue only. In practice, I’d expect there to be an If in there somewhere. In fact, my production code is closing idle connections. I don’t know if some would have been missed or not, but that’s what lead me to test.

Yes, the substitute code you mentioned is how it works, but not really since order is not guaranteed.

IMHO not.

Why should For Each be different than a For loop?

PHP: not correct. PHP does not have indexed arrays as we know them from C, Xojo, etc. Every array in PHP a dictionary in Xojo terminology. See Understanding PHP’s internal array implementation. You can test it yourself:

[code]<?php
$array = array(1, 2, 3, 4);

foreach ($array as $value) {
$index = array_search($value, $array);
if ($index == 2) {
unset($array[$index]);
}
}

print_r($array);[/code]
This will print:

Array ( [0] => 1 [1] => 2 [3] => 4 // note the 3 )

ApplceScript: same as PHP.

Good point about the difference, Eli.

So was my expectation that For Each guarantee access to each element regardless unreasonable?

BTW, to be clear, in the code I posted above, the loop will skip over two of the four elements.

I think the differrence is that languages which compile to hardware have arrays, because they address the memory directly. Index 3 means go to the array variable’s memory location and jump 3 memory cells further.

In this case the best thing is to process the array from the last cell.

It would be good to have something like for each i as integer in arr from last

or previous instead of next to suggest the starting point and step.

This doesn’t make sense IMO. For Each is treating an array as a dictionary. The order of how the loop iterates over the elements is not guaranteed. If you want to iterate an array from the last to the first, use a For i As Integer = arr.Ubound DownTo 0 loop.

My understanding too is that for…each is not the same as for i = 0 to array.ubound.

So I would consider it a bug.

The built-in For Each … Next is syntactic sugar for a For … Next loop, where one does not care about the order of the traversal, and one does no’t need to access the key within the loop.

I can’t say whether it’s definitively a bug or not but I can say that it’s surprising behavior. I assumed the same as Kem, until his thread.

This would mean that at the beginning of the For Each the Xojo framework would have to copy the array internally. Like that:

[code] Dim arr() As Integer = Array(1, 2, 3, 4)

Dim arrCopy() As Integer
For i As Integer = 0 To arr.Ubound
arrCopy.Append(arr(i))
Next

For Each i As Integer In arrCopy
arr.Remove arr.IndexOf(i)
Next[/code]
I find that counter-intuitive.

Removing an array element within a loop changes the position (= index) of all subsequent elements immediately on each iteration.

Yep, or do some clever copy-on-write setup that increases the complexity of the array code. It seems more likely that this edge case was turned into an exception if anything were to happen.

If it is just “syntactic sugar” then the order of traversal isn’t determined in a normal for…next loop either.

If that is the case then that is a definite problem.

If that isn’t the case then it isn’t syntactic sugar.

I know order in For Each is not guaranteed, but in practice, it scans elements growing one by one, would that be in an array or a dictionary.

Syntaxic sugar indeed.

Well, it saves having to know the boundaries of the iteration.

I personally usually never employ it.

The fact that the current implementation of For/Each is similar if not exactly the same as For/Next is just that, an implementation detail. Order of traversal is not guaranteed simply because the implementation is free to change as conditions require, not because the current implementation isn’t linear.

That said, there are ways of implementing For/Each that are affected by inserting/deleting elements during the loop, and other ways that are not. There should be some mention in the documentation that you may get unpredictable results if you insert or remove elements from the array in a For/Each loop. If you need to modify the array, use For/Next where you can control the order (and iterate in reverse).

For next & for each can both be affected by changes to the object they are iterating over
Dont even have to remove items :slight_smile:

  dim a() as integer = Array ( 0 )
  
  for i as integer = 0 to a.ubound
    a.insert 0, i
  next

[quote=277475:@Kem Tekinay]
Opinions?[/quote]

I’d have expected an iterator exception

Thanks for all the comments. An IteratorException is indeed what you get if you iterate over, say, a Xojo.Core.Dictionary, so I guess that makes sense for an array too. At the very least, the documentation should be updated to make it clear that you are not guaranteed access to each element if the array changes mid-loop.