Collections: Iterating

Collections are iterated via foreach.

<?hh

namespace Hack\UserDocumentation\Collections\Iterating\Examples\FEach;

function run(): void {
  $vec = Vector {'A', 'B', 'C'};
  $map = Map {'A' => 1, 'B' => 2};
  $set = Set {800, 900, 1000};
  $pair = Pair {'A', 'B'};

  foreach ($vec as $val) {
    \var_dump($val);
  }

  foreach ($map as $key => $val) {
    \var_dump($key);
    \var_dump($val);
  }

  foreach ($set as $val) {
    \var_dump($val);
  }

  foreach ($pair as $key => $val) {
    \var_dump($key);
    \var_dump($val);
  }
}

run();
Output
string(1) "A"
string(1) "B"
string(1) "C"
string(1) "A"
int(1)
string(1) "B"
int(2)
int(800)
int(900)
int(1000)
int(0)
string(1) "A"
int(1)
string(1) "B"

Caveats

There are a couple of caveats to be aware of when using foreach to iterate over a collection.

Modifying the Collection

Adding or deleting an item from the collection will not affect iteration:

<?hh

namespace Hack\UserDocumentation\Collections\Iterating\Examples\Modifying;

function run(): void {
  $vec = Vector {'A', 'B', 'C'};

  foreach ($vec as $val) {
    if ($val === 'B') {
      $vec[] = 'D';
      // Affects the container...
      \var_dump($vec);
    }
    //... but not the foreach ...
    \var_dump($val);
  }
  // ... and does affect the outside
  \var_dump($vec);
}

run();
Output
string(1) "A"
object(HH\Vector)#2 (4) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
  [3]=>
  string(1) "D"
}
string(1) "B"
string(1) "C"
object(HH\Vector)#2 (4) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
  [3]=>
  string(1) "D"
}

By Reference

You cannot do a foreach by reference with collections.

foreach ($vec as &$val)

This will result in a fatal error, not even an exception.

There is a way to mimic this behavior.

<?hh

namespace Hack\UserDocumentation\Collections\Iterating\Examples\Reference;

function run(): void {
  $arr = array ('A', 'B', 'C');
  \var_dump($arr);
  /* HH_IGNORE_ERROR[4225] this is not supported in Hack, but the example is
   * still illustrative */
  foreach ($arr as &$val) {
    \var_dump($val);
    if ($val === 'B') {
      $val = 'D';
    }
    \var_dump($arr);
  }

  // How to mimic the above
  $vec = Vector {'A', 'B', 'C'};
  \var_dump($vec);
  foreach ($vec as $key => $val) {
    \var_dump($val);
    if ($val === 'B') {
      $vec[$key] = 'D';
    }
    \var_dump($vec);
  }
}

run();
Output
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}
string(1) "A"
array(3) {
  [0]=>
  &string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}
string(1) "B"
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  &string(1) "D"
  [2]=>
  string(1) "C"
}
string(1) "C"
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "D"
  [2]=>
  &string(1) "C"
}
object(HH\Vector)#1 (3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}
string(1) "A"
object(HH\Vector)#1 (3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}
string(1) "B"
object(HH\Vector)#1 (3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "D"
  [2]=>
  string(1) "C"
}
string(1) "C"
object(HH\Vector)#1 (3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "D"
  [2]=>
  string(1) "C"
}

Basically, you explicitly pull out the key/value pair of the collection and then modify the value of the key explicitly.