Rules for working with dynamic arrays and custom collection classes – Matthias Noback

Here are some rules I use for working with dynamic arrays. It’s pretty much a Style Guide for Array Design, but it didn’t feel right to add it to the Object Design Style Guide, because not every object-oriented language has dynamic arrays. The examples in this post are written in PHP, because PHP is pretty much Java (which might be familiar), but with dynamic arrays instead of built-in collection classes and interfaces.

Using arrays as lists

All elements should be of the same type

When using an array as a list (a collection of values with a particular order), every value should be of the same type:

$goodList = [ 'a', 'b'
]; $badList = [ 'a', 1

A generally accepted style for annotating the type of a list is: @var array<TypeOfElement>.
Make sure not to add the type of the index (which would always be int).

The index of each element should be ignored

PHP will automatically create new indexes for every element in the list (0, 1, 2, etc.)
However, you shouldn’t rely on those indexes, nor use them directly.
The only properties of a list that clients should rely on is that it is iterable and countable.

So feel free to use foreach and count(), but don’t use for to loop over the elements in a list:

// Good loop:
foreach ($list as $element) {
} // Bad loop (exposes the index of each element):
foreach ($list as $index => $element) {
} // Also bad loop (the index of each element should not be used):
for ($i = 0; $i < count($list); $i++) {

(In PHP, the for loop might not even work, because there may be indices missing in the list, and indices may be higher than the number of elements in the list.)

Instead of removing elements, use a filter

You may want to remove elements from a list by their index (unset()), but instead of removing elements you should use array_filter() to create a new list, without the unwanted elements.

Again, you shouldn’t rely on the index of elements, so when using array_filter() you shouldn’t use the flag parameter to filter elements based on the index, or even based on both the element and the index.

// Good filter:
array_filter( $list, function (string $element): bool { return strlen($element) > 2; }
); // Bad filter (uses the index to filter elements as well)
array_filter( $list, function (int $index): bool { return $index > 3; }, ARRAY_FILTER_USE_KEY
); // Bad filter (uses both the index and the element to filter elements)
array_filter( $list, function (string $element, int $index): bool { return $index > 3 || $element === 'Include'; }, ARRAY_FILTER_USE_BOTH

Using arrays as maps

When keys are relevant and they are not indices (0, 1, 2, etc.). feel free to use an array as a map (a collection from which you can retrieve values by their unique key).

All the keys should be of the same type

The first rule for using arrays as maps is that all they keys in the array should be of the same type (most common are string-type keys).

$goodMap = [ 'foo' => 'bar', 'bar' => 'baz'
]; // Bad (uses different types of keys)
$badMap = [ 'foo' => 'bar', 1 => 'baz'

All the values should be of the same type

The same goes for the values in a map: they should be of the same type.

$goodMap = [ 'foo' => 'bar', 'bar' => 'baz'
]; // Bad (uses different types of values)
$badMap = [ 'foo' => 'bar', 'bar' => 1

A generally accepted style for annotating the type of a map is: @var array<TypeOfKey, TypeOfValue>.

Maps should remain private

Lists can safely be passed around from object to object, because of their simple characteristics.
Any client can use it to loop over its elements, or count its elements, even if the list is empty.
Maps are more difficult to work with, because clients may rely on keys that have no corresponding value.
This means that in general, they should remain private to the object that manages them.

Truncated by Planet PHP, read more at the original (another 8885 bytes)