Collections#

ArrayKit collections provide an object-oriented array wrapper with:

  • dot-notation read/write

  • full ArrayAccess + IteratorAggregate + Countable behavior

  • a chainable pipeline of transformation methods

  • optional get/set hooks via HookedCollection

Available classes:

  • Infocyph\ArrayKit\Collection\Collection

  • Infocyph\ArrayKit\Collection\HookedCollection

  • Infocyph\ArrayKit\Collection\Pipeline

  • Infocyph\ArrayKit\Collection\LazyCollection

Creating Collections#

<?php
use Infocyph\ArrayKit\Collection\Collection;
use function Infocyph\ArrayKit\collect;

// Constructor
$c1 = new Collection(['a' => 1, 'b' => 2]);

// Static factories (accept array-able values)
$c2 = Collection::make(['x' => 10]);
$c3 = Collection::from(['y' => 20]);

// Namespaced helper (autoloaded by default)
$c4 = collect(['z' => 30]);

Reading and Writing#

Collection supports direct array access, dot notation, and helper methods.

<?php
use Infocyph\ArrayKit\Collection\Collection;

$c = new Collection([
    'user' => ['name' => 'Alice'],
    'active' => true,
]);

// Dot notation get/set
$name = $c->get('user.name');             // Alice
$c->set('user.email', '[email protected]');

// ArrayAccess with dot notation
$email = $c['user.email'];                // [email protected]
$c['user.role'] = 'admin';

// Multi-key fetch
$subset = $c->get(['user.name', 'user.role']);

// Existence checks
$hasName = $c->has('user.name');          // true
$hasAny = $c->hasAny(['x', 'user.role']); // true

// Append with null offset
$c[] = 'tail-value';

// Remove key
unset($c['user.role']);

Collection Utility Methods#

<?php
use Infocyph\ArrayKit\Collection\Collection;

$c = new Collection(['a' => 1, 'b' => 2]);

$all = $c->all();            // full array
$items = $c->items();        // alias of all()
$keys = $c->keys();          // ['a', 'b']
$array = $c->toArray();      // array output
$json = $c->toJson();        // JSON string
$count = $c->count();        // 2
$empty = $c->isEmpty();      // false

$c->merge(['c' => 3]);       // now a,b,c
$c->clear();                 // now empty

// Immutable-style snapshots
$copy = $c->copy();
$immutable = $c->immutable();

Iteration and Interfaces#

Collection is directly iterable.

<?php
use Infocyph\ArrayKit\Collection\Collection;

$c = new Collection(['a' => 1, 'b' => 2]);

foreach ($c as $key => $value) {
    // $key, $value
}

// Supports json_encode() through JsonSerializable
$json = json_encode($c);

HookedCollection#

HookedCollection extends Collection and adds per-key hooks.

<?php
use Infocyph\ArrayKit\Collection\HookedCollection;

$c = new HookedCollection(['name' => 'alice', 'user' => ['city' => 'dhaka']]);

// Run callback(s) when reading key
$c->onGet('name', fn ($v) => strtoupper((string) $v));

// Run callback(s) when setting key
$c->onSet('role', fn ($v) => "Role: $v");

// Dot-notation hooks are supported
$c->onGet('user.city', fn ($v) => ucfirst((string) $v));

echo $c['name'];      // ALICE
$c['role'] = 'admin';
echo $c['role'];      // Role: admin
echo $c['user.city']; // Dhaka

Pipeline Basics#

Every transformation method is exposed through Pipeline. You can start it either with process() or directly by calling pipeline methods on collection (via __call). Pipeline methods mutate the current collection instance and return that same instance for chaining. Use copy() or immutable() before chaining when you need functional-style non-mutating behavior. You can also force immutable-style pipeline entry using immutableProcess() / pipeImmutable().

<?php
use Infocyph\ArrayKit\Collection\Collection;

$c = Collection::make([1, 2, 3, 4, 5]);

// Dynamic passthrough to pipeline:
$result = $c->filter(fn ($v) => $v > 2)
    ->map(fn ($v) => $v * 10)
    ->all();

// Explicit pipeline:
$sum = $c->process()->sum();

Pipeline Methods by Category#

Selection and filtering:

  • only(), except()

  • filter(), reject()

  • where(), whereCallback()

  • whereIn(), whereNotIn(), whereNull(), whereNotNull()

  • between(), whereBetween()

  • whereLike(), whereStartsWith(), whereEndsWith(), whereContains()

  • firstWhere()

  • firstWhereIn()

Slicing and positional:

  • slice(), skip(), skipWhile(), skipUntil()

  • nth(), paginate(), chunk()

Structure and reshape:

  • flatten(), flattenByKey(), collapse()

  • groupBy(), keyBy(), indexBy(), pluck(), transpose()

  • mapWithKeys(), values(), rekey()

  • wrap(), unWrap()

Ordering and uniqueness:

  • sortBy(), sortRecursive(), shuffle()

  • sortByMany()

  • unique(), duplicates(), uniqueBy(), duplicatesBy(), partition()

  • intersect(), diff(), symmetricDiff(), same()

Terminal methods (end chain with scalar/array/bool):

  • sum(), min(), max(), first(), last(), reduce()

  • any(), countBy(), median(), mode(), minBy(), maxBy(), isMultiDimensional()

Flow-control helpers:

  • tap(), pipe(), when(), unless()

  • mergeRecursiveDistinct(), replaceRecursive(), overlay()

Detailed Pipeline Examples#

Filtering and set-style operations:

<?php
use Infocyph\ArrayKit\Collection\Collection;

$users = Collection::make([
    ['id' => 1, 'name' => 'Alice', 'role' => 'admin', 'age' => 30],
    ['id' => 2, 'name' => 'Bob', 'role' => 'editor', 'age' => 21],
    ['id' => 3, 'name' => 'Cara', 'role' => null, 'age' => 25],
]);

$admins = $users->where('role', 'admin')->all();
$notNullRole = $users->whereNotNull('role')->all();
$adultEditors = $users->where('age', '>=', 21)->whereIn('role', ['editor'])->all();

Slicing and paging:

<?php
use Infocyph\ArrayKit\Collection\Collection;

$list = Collection::make([10, 20, 30, 40, 50, 60]);

$page1 = $list->paginate(1, 2)->all();         // first 2 items
$everySecond = $list->nth(2)->all();
$skipped = $list->skip(3)->all();
$until40 = $list->skipUntil(fn ($v) => $v === 40)->all();

Grouping and reshaping:

<?php
use Infocyph\ArrayKit\Collection\Collection;

$rows = Collection::make([
    ['team' => 'A', 'score' => 10],
    ['team' => 'B', 'score' => 20],
    ['team' => 'A', 'score' => 30],
]);

$grouped = $rows->groupBy('team')->all();
$scores = $rows->pluck('score')->all();         // [10, 20, 30]
$sorted = $rows->sortBy('score', desc: true)->all();
$sortedMany = $rows->sortByMany([
    ['team', 'asc'],
    ['score', 'desc'],
])->all();

LazyCollection#

Use LazyCollection for generator-backed transformations over large iterables.

<?php
use Infocyph\ArrayKit\ArrayKit;

$result = ArrayKit::lazyCollection(range(1, 1000))
    ->filterLazy(fn ($v) => $v % 2 === 0)
    ->mapLazy(fn ($v) => $v * 10)
    ->take(5)
    ->all();

// [20, 40, 60, 80, 100]

Terminal calculations:

<?php
use Infocyph\ArrayKit\Collection\Collection;

$numbers = Collection::make([1, 2, 3, 4, 5]);

$sum = $numbers->process()->sum();              // 15
$median = $numbers->process()->median();        // 3
$mode = $numbers->process()->mode();            // [1,2,3,4,5] (all equal freq)
$hasEven = $numbers->process()->any(fn ($v) => $v % 2 === 0); // true

Behavior Notes#

  • Most pipeline methods return the underlying Collection for chaining.

  • Terminal methods return scalar/array/bool and stop the chain.

  • Dot-notation works in collection accessors and in HookedCollection get/set overrides.

  • merge() follows PHP array_merge semantics (string-key overwrite, numeric append/reindex).

  • paginate() throws InvalidArgumentException when page < 1 or perPage < 1.

  • flatten(0) keeps top-level values unchanged; flatten(1) flattens one level.

  • sum() and numeric min/max flows ignore non-numeric values.