Vec
composer require php-standard-library/vec
The Vec component provides functions for creating and transforming sequential, 0-indexed arrays (list<T>). Every Vec function returns a re-indexed list, making them a type-safe, predictable replacement for PHP's array_map, array_filter, array_values, and similar functions.
Unlike PHP's built-in array functions, Vec functions guarantee consistent argument order (iterable first, callback second), never preserve keys, and always produce a list<T>.
Transforming Values
use Psl\Vec;
// Map: transform every element
Vec\map([1, 2, 3], fn(int $n) => $n * 2);
// [2, 4, 6]
// Map with key: the callback receives both key and value
Vec\map_with_key(['a', 'b', 'c'], fn(int $i, string $v) => $i . ':' . $v);
// ['0:a', '1:b', '2:c']
// Filter: keep elements matching a predicate
Vec\filter([1, 2, 3, 4, 5], fn(int $n) => $n > 3);
// [4, 5]
// Filter nulls: remove null values from a list
Vec\filter_nulls([1, null, 3, null, 5]);
// [1, 3, 5]
// Filter nonnull by: keep values where the callback returns non-null (original values are preserved)
Vec\filter_nonnull_by(['hello', '', 'world'], fn(string $v) => $v !== '' ? $v : null);
// ['hello', 'world']
// Map nonnull: transform values and discard null results
Vec\map_nonnull([1, 2, 3], fn(int $v) => $v > 1 ? $v * 2 : null);
// [4, 6]
Sorting
use Psl\Str;
use Psl\Vec;
// Sort in ascending order
Vec\sort([3, 1, 2]);
// [1, 2, 3]
// Sort with a custom comparator
Vec\sort(['banana', 'apple', 'cherry'], fn($a, $b) => Str\length($a) <=> Str\length($b));
// ['apple', 'banana', 'cherry']
// Sort by a derived value
$users = [['name' => 'Charlie', 'age' => 30], ['name' => 'Alice', 'age' => 25]];
Vec\sort_by($users, fn($u) => $u['age']);
// [['name' => 'Alice', 'age' => 25], ['name' => 'Charlie', 'age' => 30]]
Combining and Splitting
use Psl\Str;
use Psl\Vec;
// Concat: join multiple iterables into one list
Vec\concat([1, 2], [3, 4], [5]);
// [1, 2, 3, 4, 5]
// Flatten: merge a list of lists into a single list
Vec\flatten([[1, 2], [3], [4, 5]]);
// [1, 2, 3, 4, 5]
// Flat map: map then flatten in one step
Vec\flat_map(['hello world', 'foo bar'], fn($s) => Str\split($s, ' '));
// ['hello', 'world', 'foo', 'bar']
// Zip: pair up elements from two lists
Vec\zip(['a', 'b', 'c'], [1, 2, 3]);
// [['a', 1], ['b', 2], ['c', 3]]
// Chunk: split into groups of a given size
Vec\chunk([1, 2, 3, 4, 5], 2);
// [[1, 2], [3, 4], [5]]
// Partition: split into two lists based on a predicate
Vec\partition([1, 2, 3, 4, 5], fn($n) => ($n % 2) === 0);
// [[2, 4], [1, 3, 5]]
Generating and Reshaping
use Psl\Vec;
// Range: generate a sequence of numbers
Vec\range(1, 5); // [1, 2, 3, 4, 5]
Vec\range(0.0, 1.0, 0.5); // [0.0, 0.5, 1.0]
// Reproduce: generate values using a factory
Vec\reproduce(3, fn(int $i) => $i * $i);
// [1, 4, 9]
// Unique: remove duplicate values
Vec\unique([1, 2, 2, 3, 3, 3]);
// [1, 2, 3]
// Reverse: flip the order
Vec\reverse([1, 2, 3]);
// [3, 2, 1]
// Enumerate: convert key-value pairs into a list of [key, value] tuples
Vec\enumerate(['a' => 1, 'b' => 2]);
// [['a', 1], ['b', 2]]
// Keys/Values: extract keys or values as a list
Vec\keys(['x' => 10, 'y' => 20]); // ['x', 'y']
Vec\values(['x' => 10, 'y' => 20]); // [10, 20]
Key Difference from Dict
Vec always re-indexes the result. If you filter an associative array with Vec\filter, the keys are discarded and the result is a sequential list<T>. If you need to preserve keys, use Dict\filter instead.
use Psl\Dict;
use Psl\Vec;
$data = ['a' => 1, 'b' => 2, 'c' => 3];
Vec\filter($data, fn($v) => $v > 1);
// [2, 3] -- keys dropped, re-indexed as list
Dict\filter($data, fn($v) => $v > 1);
// ['b' => 2, 'c' => 3] -- keys preserved
See src/Psl/Vec/ for the full API.