Thread overview
Iterate over an array while mutating it?
Mar 27, 2014
Anh Nhan
Mar 27, 2014
Infiltrator
Mar 27, 2014
Anh Nhan
Mar 28, 2014
bearophile
Mar 27, 2014
bearophile
Mar 28, 2014
Marc Schütz
Mar 28, 2014
Regan Heath
March 27, 2014
Hey guys,

I want to iterate over an array, while adding new entries, and have those in the iteration loop.

See here: https://gist.github.com/AnhNhan/9820226

The problem is that the foreach loop seemingly only iterates over the original array, not minding the newly added entries.

Does somebody have a solution or approach for the loop to pick up those new entries?

Anh Nhan
March 27, 2014
On Thursday, 27 March 2014 at 22:23:41 UTC, Anh Nhan wrote:
> I want to iterate over an array, while adding new entries, and have those in the iteration loop.

Depending on what you're trying to do, perhaps you would be better off just adding the entries all at once instead of iterating the array?


> See here: https://gist.github.com/AnhNhan/9820226
>
> The problem is that the foreach loop seemingly only iterates over the original array, not minding the newly added entries.

That's correct.  foreach caches (which makes it faster) but means that you cannot mutate the range whilst iterating it.


> Does somebody have a solution or approach for the loop to pick up those new entries?

Your best bet is probably a for loop:

for(int i = 0; i < arr.length; i++) { arr ~= element; ... }
March 27, 2014
Anh Nhan:

> I want to iterate over an array, while adding new entries, and have those in the iteration loop.

Don't iterate an array with foreach while you mutate it, even if you manage to make it work, the code is not clear.
I suggest to use a C-style for loop with an index and specify in the code exactly what you want to do with the array and the index. The resulting code is clear and safe.

Bye,
bearophile
March 27, 2014
On Thursday, 27 March 2014 at 22:26:37 UTC, Infiltrator wrote:
> On Thursday, 27 March 2014 at 22:23:41 UTC, Anh Nhan wrote:
>> I want to iterate over an array, while adding new entries, and have those in the iteration loop.
>
> Depending on what you're trying to do, perhaps you would be better off just adding the entries all at once instead of iterating the array?
>
>
>> See here: https://gist.github.com/AnhNhan/9820226
>>
>> The problem is that the foreach loop seemingly only iterates over the original array, not minding the newly added entries.
>
> That's correct.  foreach caches (which makes it faster) but means that you cannot mutate the range whilst iterating it.
>
>
>> Does somebody have a solution or approach for the loop to pick up those new entries?
>
> Your best bet is probably a for loop:
>
> for(int i = 0; i < arr.length; i++) { arr ~= element; ... }

On Thursday, 27 March 2014 at 22:27:18 UTC, bearophile wrote:
> Anh Nhan:
>
>> I want to iterate over an array, while adding new entries, and have those in the iteration loop.
>
> Don't iterate an array with foreach while you mutate it, even if you manage to make it work, the code is not clear.
> I suggest to use a C-style for loop with an index and specify in the code exactly what you want to do with the array and the index. The resulting code is clear and safe.
>
> Bye,
> bearophile

Ah, that the foreach caches the entries would make sense. I tried creating iterators Java-style, which did not pick up the new entries, too.

It works with for-loops. Thanks for the explanations, they helped a lot.

Anh Nhan
March 28, 2014
On Thursday, 27 March 2014 at 22:23:41 UTC, Anh Nhan wrote:
> Hey guys,
>
> I want to iterate over an array, while adding new entries, and have those in the iteration loop.
>
> See here: https://gist.github.com/AnhNhan/9820226
>
> The problem is that the foreach loop seemingly only iterates over the original array, not minding the newly added entries.
>
> Does somebody have a solution or approach for the loop to pick up those new entries?

Reallocation will only happen if there isn't sufficient space left in the array. If you know the maximum number of elements in advance, you can reserve the necessary memory:

import std.array;
arr.reserve(arr.length + number_of_new_elements);
March 28, 2014
Anh Nhan:

> Ah, that the foreach caches the entries would make sense.

What does it mean "foreach caches the entries" for you? Foreach does very little. It is light syntax sugar over a normal for loop.

Bye,
bearophile
March 28, 2014
On Thu, 27 Mar 2014 22:23:40 -0000, Anh Nhan <anhnhan@outlook.com> wrote:

> Hey guys,
>
> I want to iterate over an array, while adding new entries, and have those in the iteration loop.
>
> See here: https://gist.github.com/AnhNhan/9820226
>
> The problem is that the foreach loop seemingly only iterates over the original array, not minding the newly added entries.
>
> Does somebody have a solution or approach for the loop to pick up those new entries?

Wrap the array in an adapter class/struct which implements opApply for foreach...

import std.stdio;
import std.conv;

struct ForAdd(T)
{
  T[] data;

  this(T[] _data)  { data = _data; }

  void opOpAssign(string op : "~")(T rhs) { data ~= rhs; }

  int opApply(int delegate(ref T) dg)
  {
    int result = 0;

    for (int i = 0; i < data.length; i++)
    {
      result = dg(data[i]);
      if (result)
        break;
    }

    return result;
  }
}

int main(string[] args)
{
  string[] test;

  for(int i = 0; i < 5; i++)
    test ~= to!string(i);

  auto adder = ForAdd!string(test);
  foreach(string item; adder)
  {
    writefln("%s", item);
    if (item == "2")
      adder ~= "5";
    if (item == "4")
      adder ~= "6";
    if (item == "5")
      adder ~= "7";
  }

  return 0;
}

R

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/