judahnator/iterable-collection

A fluent interface for working with iterables and generators.

v1.4.0 2022-04-10 18:58 UTC

This package is auto-updated.

Last update: 2024-04-11 06:16:15 UTC


README

pipeline status coverage report

The Gist

This library allows you to use a fluent interface to manipulate iterable items.

The goal was to have at minimum feature parity with all the array_* functions, but the library falls a bit short of that goal due to a limitation of being unable to rewind generators. For that reason, some functions have been deliberately excluded.

Of course, if you can think of a good way to implement said functions feel free to submit a pull request.

A few additional functions were added as well that do not exist as native array_* methods.

Usage

Object Creation:

$o = new IterableCollection(['foo' => 'bar']);
// or
$o = IterableCollection::wrap(['foo' => 'bar']);

Iteration:

foreach (IterableCollection::wrap(['foo' => 'bar']) as $key => $value) {
    // use $key and $value
}

Check All True:

IterableCollection::wrap([true, false, true])->all();
# false

IterableCollection::wrap([true, true, true])->all();
# true

IterableCollection::wrap([1, 2, 'a'])->all('is_numeric');
# false

Check Any True:

IterableCollection::wrap([true, false, true])->any();
# true

IterableCollection::wrap(['a', 'b', 'c'])->any(fn ($i) => $i === 'd');
# false

Change Key Case:

$o = IterableCollection::wrap(['Hello' => 'World!']);

$o->changeKeyCase(CASE_LOWER);
# IterableCollection {
#   hello: "World!",
# }

$o->changeKeyCase(CASE_UPPER);
# IterableCollection {
#   HELLO: "World!",
# }

Chunk:

$o = IterableCollection::wrap(range(1,4));

// No preserve keys (default)
$o->chunk(2);
# IterableCollection {
#   0: [
#     1,
#     2,
#   ],
#   1: [
#     3,
#     4,
#   ],
# }

// Preserve keys
$o->chunk(2, true);
# IterableCollection {
#   0: [
#     0 => 1,
#     1 => 2,
#   ],
#   1: [
#     2 => 3,
#     3 => 4,
#   ],
# }

Column:

$o = IterableCollection::wrap([
    ['id' => 123, 'name' => 'Judah Wright',],
    ['id' => 456, 'name' => 'Jane Doe',],
]);

// Without column key (default)
$o->column('name');
# IterableCollection {
#   0: "Judah Wright",
#   1: "Jane Doe",
# }

// With a column key
$o->column('name', 'id');
# IterableCollection {
#   123: "Judah Wright",
#   456: "Jane Doe",
# }

Count:

IterableCollection::wrap([1, 2, 3])->count();
# 3

Combine:

$keys = ['foo', 'bing'];
$values = ['bar', 'baz'];
IterableCollection::wrap($keys)->combine($values);
# IterableCollection {
#   foo: "bar",
#   bing: "baz",
# }

Count Values:

IterableCollection::wrap(['foo', 'bar', 'foo', 'baz'])->countValues();
# IterableCollection {
#   foo: 2,
#   bar: 1,
#   baz: 1,
# }

Fill Keys:

IterableCollection::wrap(['a', 'b', 'c'])->fillKeys('foo');
# IterableCollection {
#   a: "foo",
#   b: "foo",
#   c: "foo",
# }

Filter:

$inputs = [true, false, null, "Hello!"];

// Without a callback (default) returns keeps the "truthy" values
IterableCollection::wrap($inputs)->filter();
# IterableCollection {
#   0: true,
#   3: "Hello!",
# }

// With a callback you can do your own evaluation
IterableCollection::wrap($inputs)->filter(
    static fn ($value): bool => !is_null($value)
);
# IterableCollection {
#   0: true,
#   1: false,
#   3: "Hello!",
# }

Flip:

IterableCollection::wrap(['key' => 'value'])->flip();
# IterableCollection {
#   value: "key",
# }

In:

$o = IterableCollection::wrap(['foo', 'bar']);

$o->in('foo');
# true

$o->in('baz');
# false

Key Exists:

$o = IterableCollection::wrap(['foo' => 'bar', 'bing' => 'baz']);

$o->keyExists('foo');
# true

$o->keyExists('asdf');
# false

Keys:

IterableCollection::wrap(['foo' => 'bar', 'bing' => 'baz'])->keys();
# IterableCollection {
#   0: "foo",
#   1: "bing",
# }

Map:

$o = IterableCollection::wrap(['John' => 'Smith', 'Jane' => 'Doe']);
$names = $o->map(
    fn ($firstname, $lastname): string => "{$firstname} {$lastname}"
);
# IterableCollection {
#   0: "Smith John",
#   1: "Doe Jane",
# }

Reduce:

$o = IterableCollection::wrap([1, 2, 3]);
$o->reduce(static fn (int $carry, int $item): int => $carry + $item, 0);
# 6

To Array:

$iterator = new ArrayIterator(['foo' => 'bar']);
print_r(IterableCollection::wrap($iterator)->toArray());
# Array
# (
#     [foo] => bar
# )

Values:

IterableCollection::wrap(['key1' => 'value1', 'key2' => 'value2'])->values();
# IterableCollection {#2519
#   0: "value1",
#   1: "value2",
# }

Walk Recursive:

$tree = [
    'limb1' => [
        'branch1' => 'leaf1', 
        'branch2' => 'leaf2',
    ], 
    'limb2' => 'leaf3'
];
IterableCollection::wrap($tree)->walkRecursive(
    fn ($item, $key, $userdata) => "{$userdata} - {$key} - {$item}",
    'tree',
);
# IterableCollection {
#   0: "tree - branch1 - leaf1",
#   1: "tree - branch2 - leaf2",
#   2: "tree - limb2 - leaf3",
# }

Walk:

$o = IterableCollection::wrap(['Bears', 'Beets', 'Battlestar Galactica']);
$o->walk(
    function (&$item, &$key, int &$userdata): void {
        $item = strtoupper($item);
        $key = ['a', 'b', 'c'][$userdata];    
        $userdata++;
    },
    0,
);
# IterableCollection {
#   a: "BEARS",
#   b: "BEETS",
#   c: "BATTLESTAR GALACTICA",
# }

Deliberately Excluded Functions

Items that would require the input to implement the Iterator class, such as:

Items that would require some form of indexing, ordering, or searching. These include:

Some functions responsible for creating a new list, such as: