Array lists
An array list is an ordered, keyed collection of values. Collection ships two variants: the mutable ArrayList and the immutable ReadonlyArrayList. Both implement ArrayAccess, Countable, IteratorAggregate and JsonSerializable, and both share the same set of read and transformation operations through the ArrayListable trait.
Creating a list
Construct a list directly from a plain array, or use of() to build one from any iterable, including another list or a Traversable.
<?php
declare(strict_types=1);
use Raxos\Collection\ArrayList;
$fromArray = new ArrayList([1, 2, 3]);
$fromIterable = ArrayList::of(['a', 'b', 'c']);
$fromGenerator = ArrayList::of((static function () {
yield 1;
yield 2;
})());When the source is a list (a zero indexed array), of() reindexes the values. On typed subclasses it also validates every item, which is covered on the typed lists page.
Array access and iteration
A list behaves like a native array for reading, counting and iterating.
$list = new ArrayList(['first', 'second', 'third']);
$list[0]; // 'first'
isset($list[5]); // false
count($list); // 3
foreach ($list as $index => $value) {
// 0 => 'first', 1 => 'second', 2 => 'third'
}Because ArrayList is mutable, you can also write and unset through array access.
$list[] = 'fourth';
$list[0] = 'zeroth';
unset($list[1]);Adding and removing items
ArrayList exposes stack and queue style operations.
$list = new ArrayList([2, 3]);
$list->append(4); // [2, 3, 4]
$list->prepend(1); // [1, 2, 3, 4]
$last = $list->pop(); // 4, list is now [1, 2, 3]
$first = $list->shift(); // 1, list is now [2, 3]Transforming a list
Transformation operations never mutate the receiver. Each one returns a new instance of the same class, so calls chain together.
$people = ArrayList::of([
['name' => 'Alice', 'age' => 30],
['name' => 'Bob', 'age' => 17],
['name' => 'Carol', 'age' => 42],
]);
$names = $people
->filter(static fn(array $person): bool => $person['age'] >= 18)
->sort(static fn(array $left, array $right): int => $left['age'] <=> $right['age'])
->map(static fn(array $person): string => $person['name']);
$names->toArray(); // ['Alice', 'Carol']Some of the most used operations:
map(callable $fn),filter(callable $predicate)andreduce(callable $fn, mixed $initial = null)for the classic functional trio.first()andlast(), each optionally taking a predicate and a default.chunk(int $size)to split into a list of lists, andgroupBy(callable $fn)to group items by a computed key.sort(callable $compare),reverse(),shuffle()andunique()for reordering and deduplication.slice(int $offset, ?int $length = null),splice(),merge()anddiff()for structural changes.contains(),some(),every(),isEmpty()andsearch()for inspection.
See the ArrayList reference for the complete list.
Converting between implementations
convertTo() rebuilds a list as a different ArrayListInterface implementation, for example to turn a plain list of strings into a StringArrayList and gain its helpers.
use Raxos\Collection\{ArrayList, StringArrayList};
$labels = new ArrayList(['red', 'green', 'blue']);
$strings = $labels->convertTo(StringArrayList::class);
$strings->join(' / '); // 'red / green / blue'Immutable lists
ReadonlyArrayList offers the same read and transformation operations as ArrayList, but any write through array access throws a CollectionImmutableException.
<?php
declare(strict_types=1);
use Raxos\Collection\ReadonlyArrayList;
use Raxos\Collection\Error\CollectionImmutableException;
$list = ReadonlyArrayList::of([1, 2, 3]);
$list->map(static fn(int $number): int => $number * 2); // fine, returns a new list
try {
$list[0] = 99; // throws
} catch (CollectionImmutableException $exception) {
// The collection is immutable.
}Transformation methods still return new ReadonlyArrayList instances, so you keep chaining while the original data stays untouched.
Inspecting keys and values
keys() and values() return a new list, of the same class as the receiver, holding respectively the array keys and the array values of the current list. They are handy when a list carries string or non sequential keys and you want just one side of the pairs.
$scores = new ArrayList(['alice' => 30, 'bob' => 17, 'carol' => 42]);
$scores->keys(); // ArrayList of ['alice', 'bob', 'carol']
$scores->values(); // ArrayList of [30, 17, 42]firstKey() and lastKey() return the raw array key of the first or last entry, or null when the list is empty. Unlike first() and last(), which return the value, these return the key itself.
$scores->firstKey(); // 'alice'
$scores->lastKey(); // 'carol'
(new ArrayList())->firstKey(); // nullIterating without transforming
each() runs a callback for every item and key purely for its side effects and returns the same instance, so it does not build a new list the way map() does. Use it when you want to act on each item, for example asserting on it or logging it, while keeping the original list to carry on with.
public function each(callable $fn): static$products = ArrayList::of([
['id' => 1, 'name' => 'Keyboard'],
['id' => 2, 'name' => 'Mouse'],
]);
$products->each(static function (array $product, int $index): void {
assert($product['id'] > 0);
});
// $products is the same instance, unchanged.The callback receives the value first and the key second.
Extracting and reshaping nested data
column() pulls a single field out of every item, where each item is an array or an object. It accepts one or more keys as a variadic, and each extra key drills one level deeper into the result of the previous one.
public function column(string|int ...$columns): static$products = ArrayList::of([
['id' => 1, 'name' => 'Keyboard'],
['id' => 2, 'name' => 'Mouse'],
]);
$products->column('id'); // ArrayList of [1, 2]
$products->column('name'); // ArrayList of ['Keyboard', 'Mouse']collapse() flattens one level of nesting into a single flat list. Nested plain arrays and nested ArrayList items are merged in order, while scalar items are kept as is.
public function collapse(): static$nested = new ArrayList([
[1, 2],
[3, 4],
new ArrayList([5, 6]),
]);
$nested->collapse(); // ArrayList of [1, 2, 3, 4, 5, 6]Picking specific keys with only
only() maps over the list and reduces every item to the given keys. For an array item it keeps only those keys, for an object that defines its own only() method it delegates to that method, and any other item is passed through unchanged.
public function only(array $keys): static$people = ArrayList::of([
['id' => 1, 'name' => 'Alice', 'age' => 30],
['id' => 2, 'name' => 'Bob', 'age' => 17],
]);
$people->only(['id', 'name']);
// ArrayList of [
// ['id' => 1, 'name' => 'Alice'],
// ['id' => 2, 'name' => 'Bob'],
// ]Cloning a list
clone() returns a fresh instance of the same class holding the same data. It is a one line way to branch off a copy before further in place mutation on an ArrayList, so the original stays as it was.
public function clone(): static$original = new ArrayList([1, 2, 3]);
$copy = $original->clone();
$copy->append(4); // $copy is [1, 2, 3, 4], $original stays [1, 2, 3]