Collections: Read Write

Like arrays, you can use the square bracket syntax ([]) to read and write from Hack collections.

Reading

There are a few ways to read from a collection. You can use [], call methods like Set::contains() and Vector::containsKey() or use functions like isset() and empty().

Square bracket Syntax []

You can use square bracket syntax to read from collections.

echo $coll[$key];
<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\ReadSquareBrack;

function run(): void {
  $vec = Vector {'A', 'B', 'C'};
  var_dump($vec[1]); // 'B'

  $map = Map {'A' => 1, 'B' =>2};
  var_dump($map['B']); // 2

  $pair = Pair {100, 200};
  var_dump($pair[0]); // 100
}

run();
Output
string(1) "B"
int(2)
int(100)

Sets do not have keys, so it is a bit strange to use the square bracket syntax for reading, although you can do it at runtime. The Hack typechecker will throw an error, however. You basically replace $key above with the value in the set you are looking for. Since it is confusing, it is just better to use Set::contains() instead.

<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\ReadSqBrackSet;

function run(): void {
  $set = Set {100, 200, 300, 400};
  // Using [] with a Set is allowed at runtime, but confusing.
  // You are checking for existence of a value, not a key, since sets don't
  // have keys. The Hack typechecker will throw an error here.
  var_dump($set[100]); // Outputs 100. Seems strange that you would do this.
  // Normally, you are checking a set if it contains a value
  var_dump($set->contains(100)); // true
}

run();
Output
int(100)
bool(true)

If you use square brackets to read from a collection and the key does not exist in the collection (or the value in the case of a Set), then an OutOfBoundsExceptionis thrown.

<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\ReadOutOfBounds;

function run(): void {
  $set = Set {100, 200, 300, 400};
  $map = Map {'A' => 1, 'B' =>2};
  try {
    $a = $set[999];
  } catch (\OutOfBoundsException $ex) {
    var_dump($ex->getMessage()); // 999 not a defined key
  }
  try {
    $a = $map['C'];
  } catch (\OutOfBoundsException $ex) {
    var_dump($ex->getMessage()); // 'C' not a defined key
  }
}

run();
Output
string(30) "Integer key 999 is not defined"
string(29) "String key "C" is not defined"

Retrieving Functions

In addition to using square bracket syntax [] to get a value from a collection, there are methods on Vector, Map and Pair to, given a key, get a value. Since a Set does not have keys, these methods do not apply.

  • at() (e.g., Vector::at()) retrieves the value at the specified key; if the provided key is not found, an OutOfBoundsException is thrown.
  • get() (e.g. Vector::get()) retrieves the values at the specified key; if the provided key is not found, null is returned. Use get() to safely try to get a value from a key that possibly does not exist.

Containing Functions

To test whether a key exists in a Vector, Map or Pair, you can also use the Vector::containsKey(), Map::containsKey(), and Pair::containsKey() methods, respectively. Using the containsKey() methods as opposed to [] allows you to avoid the possibility of an OutOfBoundsException being thrown.

For a Set, use Set::contains() to check if a value exists in the set.

<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\ReadContains;

function run(): void {
  $vec = Vector {'A', 'B', 'C'};
  if ($vec->containsKey(2)) {
    var_dump($vec[2]); // 'C'
  } else {
    var_dump('Move on, but at least no OutOfBoundsException');
  }
  // We can avoid of OutOfBoundsException by using contains()
  if ($vec->containsKey(3)) {
    var_dump($vec[3]); // Doesn't exist
  } else {
    var_dump('Move on, but at least no OutOfBoundsException');
  }
}

run();
Output
string(1) "C"
string(45) "Move on, but at least no OutOfBoundsException"

Note: Using Map::contains() is synonymous with Map::containsKey().

You can also use isset() or empty() for key (or value for sets) testing as well. Just note that these functions are not supported in Hack's strict mode.

<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\ReadIsset;

// Be aware, isset() and empty() fail typechecking in strict mode.
function run(): void {
  $vec = Vector {'A', 'B', 'C'};
  if (isset($vec[2])) {
    var_dump($vec[2]); // 'C'
  } else {
    var_dump('Move on, but at least no OutOfBoundsException');
  }
  // We can avoid of OutOfBoundsException by using isset()
  if (isset($vec[3])) {
    var_dump($vec[3]); // Doesn't exist
  } else {
    var_dump('Move on, but at least no OutOfBoundsException');
  }
}

run();
Output
string(1) "C"
string(45) "Move on, but at least no OutOfBoundsException"

Note: You can use other array functions like array_key_exists(), array_keys() with Hack collections as well.

Writing

Square Bracket Syntax []

You can append values to Vectors and Sets using an empty [].

For Maps you have two ways to append a value using [].

$map[newKey] = value;
$map[] = Pair {key, value};

You cannot append a value to a Pair. An InvalidOperationException will be thrown and the Hack typechecker will throw an error.

<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\WriteSqBracket;

function run(): void {
  $vec = Vector {'A', 'B', 'C'};
  $vec[] = 'D'; // The index will be 3
  var_dump($vec[3]); // 'D'

  $map = Map {'A' => 1, 'B' =>2};
  $map['C'] = 3;
  $map[] = Pair {'D', 4}; // 'D' is key, 4 is value
  var_dump($map['C']); // 3
  var_dump($map['D']); // 4

  $pair = Pair {100, 200};
  try {
    // We cannot add a value to a pair, for obvious reasons
    $pair[] = 300;
  } catch (\InvalidOperationException $ex) {
    var_dump($ex->getMessage());
  }
}

run();
Output
string(1) "D"
int(3)
int(4)
string(46) "Cannot modify immutable object of type HH\Pair"

Appending Methods

You can also use Vector::add() or Set::add() to pass a value to be added to a Vector or Set, respectively. And for Maps, you can use Map::add(), but pass Pair with a key and value.

Deleting Methods

To remove a value from a Vector, use Vector::removeKey(), passing in the index n to remove. This will remove that index, the value associated with that index, and finally shift down one all indices n + 1 to the final index.

To remove a value from a Map, you also use Map::removeKey(), passing in the key to remove.

To remove a value from a Set, use Set::remove(), passing in the value in the set to remove.

You cannot remove values from a Pair.

<?hh

namespace Hack\UserDocumentation\Collections\ReadWrite\Examples\Delete;

function run(): void {
  $vec = Vector {'A', 'B', 'C'};
  $vec->removeKey(1);
  var_dump($vec[1]); // 'C' because its index is shifted back one

  $map = Map {'A' => 1, 'B' =>2};
  $map->removeKey('B');
  var_dump($map->containsKey('B'));

  $set = Set {1000, 2000, 3000, 4000};
  $set->remove(3000);
  var_dump($set);
}

run();
Output
string(1) "C"
bool(false)
object(HH\Set)#3 (3) {
  int(1000)
  int(2000)
  int(4000)
}

Note: You can use unset() on Map and Set, but not on Vector. Vectors shift the indices after a removal, but arrays do not, and unset() was originally meant for arrays.