Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3766663
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
34 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/omnitools/src/Collections/BaseCollection.php b/omnitools/src/Collections/BaseCollection.php
index 25b2de4..69b1901 100644
--- a/omnitools/src/Collections/BaseCollection.php
+++ b/omnitools/src/Collections/BaseCollection.php
@@ -1,28 +1,34 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Collections;
abstract class BaseCollection {
///
/// Constructors
///
public static abstract function from (iterable $items) : static;
+ ///
+ /// Constants
+ ///
+
+ const CB_ZERO_ARG = "Callback function should have at least one argument";
+
///
/// Getters
///
public abstract function toArray () : array;
///
/// Properties
///
public abstract function count () : int;
public abstract function isEmpty () : bool;
}
diff --git a/omnitools/src/Collections/HashMap.php b/omnitools/src/Collections/HashMap.php
index d6c65ec..934f1fa 100644
--- a/omnitools/src/Collections/HashMap.php
+++ b/omnitools/src/Collections/HashMap.php
@@ -1,191 +1,221 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Collections;
use Keruald\OmniTools\Reflection\CallableElement;
use ArrayIterator;
use InvalidArgumentException;
use Traversable;
/**
* An associative array allowing the use of chained
*
*
* This class can be used as a service container,
* an application context, to store configuration.
*/
class HashMap extends BaseMap {
///
/// Properties
///
private array $map;
///
/// Constructor
///
public function __construct (iterable $iterable = []) {
if (is_array($iterable)) {
$this->map = (array)$iterable;
return;
}
foreach ($iterable as $key => $value) {
$this->map[$key] = $value;
}
}
public static function from (iterable $items) : static {
return new self($items);
}
///
/// Interact with map content at key level
///
public function get (mixed $key) : mixed {
if (!array_key_exists($key, $this->map)) {
throw new InvalidArgumentException("Key not found.");
}
return $this->map[$key];
}
public function getOr (mixed $key, mixed $defaultValue) : mixed {
return $this->map[$key] ?? $defaultValue;
}
public function set (mixed $key, mixed $value) : static {
if ($key === null) {
throw new InvalidArgumentException("Key can't be null");
}
$this->map[$key] = $value;
return $this;
}
public function unset (mixed $key) : static {
unset($this->map[$key]);
return $this;
}
public function has (mixed $key) : bool {
return array_key_exists($key, $this->map);
}
public function contains (mixed $value) : bool {
return in_array($value, $this->map);
}
///
/// Interact with collection content at collection level
///
public function count () : int {
return count($this->map);
}
public function isEmpty () : bool {
return $this->count() === 0;
}
public function clear () : self {
$this->map = [];
return $this;
}
/**
* Merge the specified map with the current map.
*
* If a key already exists, the value already set is kept.
*
* @see update() when you need to update with the new value.
*/
public function merge (iterable $iterable) : self {
foreach ($iterable as $key => $value) {
$this->map[$key] ??= $value;
}
return $this;
}
/**
* Merge the specified map with the current bag.
*
* If a key already exists, the value is updated with the new one.
*
* @see merge() when you need to keep old value.
*/
public function update (iterable $iterable) : self {
foreach ($iterable as $key => $value) {
$this->map[$key] = $value;
}
return $this;
}
/**
* Gets a copy of the internal map.
*
* Scalar values (int, strings) are cloned.
* Objects are references to a specific objet, not a clone.
*
* @return array<string, mixed>
*/
public function toArray () : array {
return $this->map;
}
///
/// HOF
///
public function map (callable $callable) : self {
return new self(array_map($callable, $this->map));
}
public function filter (callable $callable) : self {
$argc = (new CallableElement($callable))->countArguments();
if ($argc === 0) {
- throw new InvalidArgumentException(
- "Callback should have at least one argument"
- );
+ throw new InvalidArgumentException(self::CB_ZERO_ARG);
}
- $mode = (int)($argc > 1);
+ $mode = (int)($argc > 1);
return new self(
array_filter($this->map, $callable, $mode)
);
}
public function mapKeys (callable $callable) : self {
$mappedMap = [];
foreach ($this->map as $key => $value) {
$mappedMap[$callable($key)] = $value;
}
return new self($mappedMap);
}
+ public function mapValuesAndKeys (callable $callable) : self {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ $newMap = [];
+ foreach ($this->map as $key => $value) {
+ [$newKey, $newValue] = match($argc) {
+ 0 => throw new InvalidArgumentException(self::CB_ZERO_ARG),
+ 1 => $callable($value),
+ default => $callable($key, $value),
+ };
+ $newMap[$newKey] = $newValue;
+ }
+
+ return new self($newMap);
+ }
+
+ public function flatMap (callable $callable) : self {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ $newMap = new self;
+ foreach ($this->map as $key => $value) {
+ $toAdd = match($argc) {
+ 0 => throw new InvalidArgumentException(self::CB_ZERO_ARG),
+ 1 => $callable($value),
+ default => $callable($key, $value),
+ };
+ $newMap->update($toAdd);
+ }
+
+ return $newMap;
+ }
+
public function filterKeys (callable $callable) : self {
return new self(
array_filter($this->map, $callable, ARRAY_FILTER_USE_KEY)
);
}
///
/// IteratorAggregate
///
public function getIterator () : Traversable {
return new ArrayIterator($this->map);
}
}
diff --git a/omnitools/src/Collections/Vector.php b/omnitools/src/Collections/Vector.php
index ce4ec60..5eb8ffe 100644
--- a/omnitools/src/Collections/Vector.php
+++ b/omnitools/src/Collections/Vector.php
@@ -1,276 +1,292 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Collections;
use Keruald\OmniTools\Reflection\CallableElement;
use Keruald\OmniTools\Strings\Multibyte\OmniString;
use ArrayAccess;
use ArrayIterator;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;
class Vector extends BaseCollection implements ArrayAccess, IteratorAggregate {
///
/// Properties
///
private array $items;
///
/// Constructors
///
public function __construct (iterable $items = []) {
if (is_array($items)) {
$this->items = $items;
return;
}
foreach ($items as $item) {
$this->items[] = $item;
}
}
public static function from (iterable $items) : static {
return new self($items);
}
///
/// Specialized constructors
///
/**
* Constructs a new instance of a vector by exploding a string
* according a specified delimiter.
*
* @param string $delimiter The substring to find for explosion
* @param string $string The string to explode
* @param int $limit If specified, the maximum count of vector elements
* @return static
*/
public static function explode (string $delimiter, string $string,
int $limit = PHP_INT_MAX) : self {
// There is some discussion to know if this method belongs
// to Vector or OmniString.
//
// The advantage to keep it here is we can have constructs like:
// Vector::explode(",", "1,1,2,3,5,8,13")
// ->toIntegers()
// >map(function($n) { return $n * $n; })
// ->toArray();
//
// In this chaining, it is clear we manipulate Vector methods.
return (new OmniString($string))
->explode($delimiter, $limit);
}
///
/// Interact with collection content at key level
///
public function get (int $key) : mixed {
if (!array_key_exists($key, $this->items)) {
throw new InvalidArgumentException("Key not found.");
}
return $this->items[$key];
}
public function getOr (int $key, mixed $defaultValue) : mixed {
return $this->items[$key] ?? $defaultValue;
}
public function set (int $key, mixed $value) : static {
$this->items[$key] = $value;
return $this;
}
public function unset (int $key) : static {
unset($this->items[$key]);
return $this;
}
public function contains (mixed $value) : bool {
return in_array($value, $this->items);
}
///
/// Interact with collection content at collection level
///
public function count () : int {
return count($this->items);
}
public function isEmpty () : bool {
return $this->count() === 0;
}
public function clear () : self {
$this->items = [];
return $this;
}
public function push (mixed $item) : self {
$this->items[] = $item;
return $this;
}
/**
* Append all elements of the specified iterable
* to the current vector.
*
* If a value already exists, the value is still added
* as a duplicate.
*
* @see update() when you need to only add unique values.
*/
public function append (iterable $iterable) : self {
foreach ($iterable as $value) {
$this->items[] = $value;
}
return $this;
}
/**
* Append all elements of the specified iterable
* to the current vector.
*
* If a value already exists, it is skipped.
*
* @see append() when you need to always add everything.
*/
public function update (iterable $iterable) : self {
foreach ($iterable as $value) {
if (!$this->contains($value)) {
$this->items[] = $value;
}
}
return $this;
}
/**
* Gets a copy of the internal vector.
*
* Scalar values (int, strings) are cloned.
* Objects are references to a specific objet, not a clone.
*
* @return array
*/
public function toArray () : array {
return $this->items;
}
///
/// HOF :: generic
///
public function map (callable $callable) : self {
return new self(array_map($callable, $this->items));
}
public function filter (callable $callable) : self {
$argc = (new CallableElement($callable))->countArguments();
if ($argc === 0) {
throw new InvalidArgumentException(
"Callback should have at least one argument"
);
}
$mode = (int)($argc > 1);
return new self(array_filter($this->items, $callable, $mode));
}
public function mapKeys (callable $callable) : self {
$mappedVector = [];
foreach ($this->items as $key => $value) {
$mappedVector[$callable($key)] = $value;
}
return new self($mappedVector);
}
+ public function flatMap (callable $callable) : self {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ $newMap = new self;
+ foreach ($this->items as $key => $value) {
+ $toAdd = match($argc) {
+ 0 => throw new InvalidArgumentException(self::CB_ZERO_ARG),
+ 1 => $callable($value),
+ default => $callable($key, $value),
+ };
+ $newMap->append($toAdd);
+ }
+
+ return $newMap;
+ }
+
public function filterKeys (callable $callable) : self {
return new self(
array_filter($this->items, $callable, ARRAY_FILTER_USE_KEY)
);
}
///
/// HOF :: specialized
///
public function toIntegers () : self {
array_walk($this->items, ArrayUtilities::toIntegerCallback());
return $this;
}
public function implode(string $delimiter) : OmniString {
return new OmniString(implode($delimiter, $this->items));
}
///
/// ArrayAccess
/// Interface to provide accessing objects as arrays.
///
private static function ensureOffsetIsInteger (mixed $offset) {
if (is_int($offset)) {
return;
}
throw new InvalidArgumentException(
"Offset of a vector must be an integer."
);
}
public function offsetExists (mixed $offset) : bool {
self::ensureOffsetIsInteger($offset);
return array_key_exists($offset, $this->items);
}
public function offsetGet (mixed $offset) : mixed {
self::ensureOffsetIsInteger($offset);
return $this->get($offset);
}
public function offsetSet (mixed $offset, mixed $value) : void {
if ($offset === null) {
$this->push($value);
return;
}
self::ensureOffsetIsInteger($offset);
$this->set($offset, $value);
}
public function offsetUnset (mixed $offset) : void {
self::ensureOffsetIsInteger($offset);
$this->unset($offset);
}
///
/// IteratorAggregate
///
public function getIterator () : Traversable {
return new ArrayIterator($this->items);
}
}
diff --git a/omnitools/tests/Collections/HashMapTest.php b/omnitools/tests/Collections/HashMapTest.php
index 8d9e214..1d7436c 100644
--- a/omnitools/tests/Collections/HashMapTest.php
+++ b/omnitools/tests/Collections/HashMapTest.php
@@ -1,329 +1,437 @@
<?php
namespace Keruald\OmniTools\Tests\Collections;
use Keruald\OmniTools\Collections\HashMap;
use PHPUnit\Framework\TestCase;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;
class HashMapTest extends TestCase {
///
/// Test set up
///
private HashMap $map;
const MAP_CONTENT = [
// Some sci-fi civilizations and author
"The Culture" => "Iain Banks",
"Radchaai Empire" => "Ann Leckie",
"Barrayar" => "Lois McMaster Bujold",
"Hainish" => "Ursula K. Le Guin",
];
protected function setUp () : void {
$this->map = new HashMap(self::MAP_CONTENT);
}
///
/// Constructors
///
public function testConstructorWithArray () {
$this->assertSame(self::MAP_CONTENT, $this->map->toArray());
}
public function testConstructorWithTraversable () {
$expected = [
"color" => "blue",
"material" => "glass",
"shape" => "sphere",
];
$iterable = new class implements IteratorAggregate {
function getIterator () : Traversable {
yield "color" => "blue";
yield "material" => "glass";
yield "shape" => "sphere";
}
};
$map = new HashMap($iterable);
$this->assertSame($expected, $map->toArray());
}
public function testFrom () {
$map = HashMap::from(self::MAP_CONTENT);
$this->assertSame(self::MAP_CONTENT, $map->toArray());
}
///
/// Getters and setters
///
public function testGet () {
$this->assertSame("Iain Banks", $this->map->get("The Culture"));
}
public function testGetWhenKeyIsNotFound () {
$this->expectException(InvalidArgumentException::class);
$this->map->get("Quuxians");
}
public function testGetOr () {
$actual = $this->map
->getOr("The Culture", "Another author");
$this->assertSame("Iain Banks", $actual);
}
public function testGetOrWhenKeyIsNotFound () {
$actual = $this->map
->getOr("Quuxians", "Another author");
$this->assertSame("Another author", $actual);
}
public function testSetWithNewKey () {
$this->map->set("Thélème", "François Rabelais");
$this->assertSame("François Rabelais",
$this->map->get("Thélème"));
}
public function testSetWithExistingKey () {
$this->map->set("The Culture", "Iain M. Banks");
$this->assertSame("Iain M. Banks",
$this->map->get("The Culture"));
}
public function testUnset() {
$this->map->unset("The Culture");
$this->assertFalse($this->map->contains("Iain Banks"));
}
public function testUnsetNotExistingKey() {
$this->map->unset("Not existing");
$this->assertEquals(4, $this->map->count());
}
public function testHas () {
$this->assertTrue($this->map->has("The Culture"));
$this->assertFalse($this->map->has("Not existing key"));
}
public function testContains () {
$this->assertTrue($this->map->contains("Iain Banks"));
$this->assertFalse($this->map->contains("Not existing value"));
}
///
/// Collection method
///
public function testCount () {
$this->assertSame(4, $this->map->count());
}
public function testClear () {
$this->map->clear();
$this->assertSame(0, $this->map->count());
}
public function testIsEmpty () : void {
$this->map->clear();
$this->assertTrue($this->map->isEmpty());
}
public function testMerge () {
$iterable = [
"The Culture" => "Iain M. Banks", // existing key
"Thélème" => "François Rabelais", // new key
];
$expected = [
// The original map
"The Culture" => "Iain Banks", // Old value should be kept
"Radchaai Empire" => "Ann Leckie",
"Barrayar" => "Lois McMaster Bujold",
"Hainish" => "Ursula K. Le Guin",
// The entries with a new key
"Thélème" => "François Rabelais",
];
$this->map->merge($iterable);
$this->assertSame($expected, $this->map->toArray());
}
public function testUpdate () {
$iterable = [
"The Culture" => "Iain M. Banks", // existing key
"Thélème" => "François Rabelais", // new key
];
$expected = [
// The original map
"The Culture" => "Iain M. Banks", // Old value should be updated
"Radchaai Empire" => "Ann Leckie",
"Barrayar" => "Lois McMaster Bujold",
"Hainish" => "Ursula K. Le Guin",
// The entries with a new key
"Thélème" => "François Rabelais",
];
$this->map->update($iterable);
$this->assertSame($expected, $this->map->toArray());
}
public function testToArray () {
$this->assertEquals(self::MAP_CONTENT, $this->map->toArray());
}
///
/// High order functions
///
public function testMap () {
$callback = function ($value) {
return "author='" . $value . "'";
};
$expected = [
"The Culture" => "author='Iain Banks'",
"Radchaai Empire" => "author='Ann Leckie'",
"Barrayar" => "author='Lois McMaster Bujold'",
"Hainish" => "author='Ursula K. Le Guin'",
];
$actual = $this->map->map($callback)->toArray();
$this->assertEquals($expected, $actual);
}
public function testMapKeys () {
$callback = function ($key) {
return "civ::" . $key;
};
$expected = [
// Some sci-fi civilizations and author
"civ::The Culture" => "Iain Banks",
"civ::Radchaai Empire" => "Ann Leckie",
"civ::Barrayar" => "Lois McMaster Bujold",
"civ::Hainish" => "Ursula K. Le Guin",
];
$actual = $this->map->mapKeys($callback)->toArray();
$this->assertEquals($expected, $actual);
}
+ public function testMapKeysAndValues () : void {
+ $callback = function ($civilization, $author) {
+ return [$author[0], "$author, $civilization"];
+ };
+
+ $expected = [
+ // Some sci-fi civilizations and author
+ "I" => "Iain Banks, The Culture",
+ "A" => "Ann Leckie, Radchaai Empire",
+ "L" => "Lois McMaster Bujold, Barrayar",
+ "U"=> "Ursula K. Le Guin, Hainish",
+ ];
+
+ $actual = $this->map->mapValuesAndKeys($callback)->toArray();
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMapKeysAndValuesForVectors () : void {
+ $callback = function ($author) {
+ return [$author[0], "author:" . $author];
+ };
+
+ $expected = [
+ // Some sci-fi civilizations and author
+ "I" => "author:Iain Banks",
+ "A" => "author:Ann Leckie",
+ "L" => "author:Lois McMaster Bujold",
+ "U" => "author:Ursula K. Le Guin",
+ ];
+
+ $actual = $this->map->mapValuesAndKeys($callback)->toArray();
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMapKeysAndValuesWithCallbackWithoutArgument() : void {
+ $this->expectException(InvalidArgumentException::class);
+
+ $callback = function () {};
+ $this->map->mapValuesAndKeys($callback);
+ }
+
+ public function testFlatMap(): void {
+ $callback = function ($key, $value) {
+ $items = explode(" ", $value);
+
+ foreach ($items as $item) {
+ yield $item => $key;
+ }
+ };
+
+ $expected = [
+ "Iain" => "The Culture",
+ "Banks" => "The Culture",
+
+ "Ann" => "Radchaai Empire",
+ "Leckie" => "Radchaai Empire",
+
+ "Lois" => "Barrayar",
+ "McMaster" => "Barrayar",
+ "Bujold" => "Barrayar",
+
+ "Ursula"=> "Hainish",
+ "K."=> "Hainish",
+ "Le"=> "Hainish",
+ "Guin"=> "Hainish",
+ ];
+
+ $actual = $this->map->flatMap($callback)->toArray();
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testFlatMapForVectors() : void {
+ $callback = function ($value) {
+ $items = explode(" ", $value);
+
+ foreach ($items as $item) {
+ yield $item => $value;
+ }
+ };
+
+ $expected = [
+ "Iain" => "Iain Banks",
+ "Banks" => "Iain Banks",
+
+ "Ann" => "Ann Leckie",
+ "Leckie" => "Ann Leckie",
+
+ "Lois" => "Lois McMaster Bujold",
+ "McMaster" => "Lois McMaster Bujold",
+ "Bujold" => "Lois McMaster Bujold",
+
+ "Ursula"=> "Ursula K. Le Guin",
+ "K."=> "Ursula K. Le Guin",
+ "Le"=> "Ursula K. Le Guin",
+ "Guin"=> "Ursula K. Le Guin",
+ ];
+
+ $actual = $this->map->flatMap($callback)->toArray();
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testFlatMapWithCallbackWithoutArgument() : void {
+ $this->expectException(InvalidArgumentException::class);
+
+ $callback = function () {};
+ $this->map->flatMap($callback);
+ }
+
public function testFilter () {
// Let's filter to keep names with 3 parts or more
$callback = function ($value) : bool {
return str_word_count($value) > 2;
};
$expected = [
// Some sci-fi civilizations and author
"Barrayar" => "Lois McMaster Bujold",
"Hainish" => "Ursula K. Le Guin",
];
$actual = $this->map->filter($callback)->toArray();
$this->assertEquals($expected, $actual);
}
public function testFilterWithKeyValueCallback () {
// Let's find civilization AND author with e inside
$expected = [
// Some sci-fi civilizations and author
"Radchaai Empire" => "Ann Leckie",
];
$callback = function ($key, $value) : bool {
return str_contains($key, "e") && str_contains($value, "e");
};
$actual = $this->map->filter($callback)->toArray();
$this->assertEquals($expected, $actual);
}
public function testFilterWithCallbackWithoutArgument() {
$this->expectException(InvalidArgumentException::class);
$callback = function () : bool { // No argument
return true;
};
$this->map->filter($callback);
}
public function testFilterKeys () {
// Let's filter to keep short civilization names
$callback = function ($key) : bool {
return str_word_count($key) == 1;
};
$expected = [
// Some sci-fi civilizations and author
"Barrayar" => "Lois McMaster Bujold",
"Hainish" => "Ursula K. Le Guin",
];
$actual = $this->map->filterKeys($callback)->toArray();
$this->assertEquals($expected, $actual);
}
///
/// ArrayAccess
///
public function testOffsetExists () : void {
$this->assertTrue(isset($this->map["The Culture"]));
$this->assertFalse(isset($this->map["Not existing"]));
}
public function testOffsetSetWithoutOffset () : void {
$this->expectException(InvalidArgumentException::class);
$this->map[] = "Another Author";
}
public function testOffsetSet () : void {
$this->map["The Culture"] = "Iain M. Banks";
$this->assertEquals("Iain M. Banks", $this->map["The Culture"]);
}
public function testOffsetUnset () : void {
unset($this->map["Barrayar"]);
$expected = [
"The Culture" => "Iain Banks",
"Radchaai Empire" => "Ann Leckie",
// "Barrayar" => "Lois McMaster Bujold", UNSET ENTRY
"Hainish" => "Ursula K. Le Guin",
];
$this->assertEquals($expected, $this->map->toArray());
}
///
/// IteratorAggregate
///
public function testGetIterator () : void {
$this->assertEquals(self::MAP_CONTENT, iterator_to_array($this->map));
}
}
diff --git a/omnitools/tests/Collections/VectorTest.php b/omnitools/tests/Collections/VectorTest.php
index 68bce70..86d3370 100644
--- a/omnitools/tests/Collections/VectorTest.php
+++ b/omnitools/tests/Collections/VectorTest.php
@@ -1,229 +1,281 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\Collections;
use Keruald\OmniTools\Collections\Vector;
use PHPUnit\Framework\TestCase;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;
class VectorTest extends TestCase {
private Vector $vector;
protected function setUp () : void {
$this->vector = new Vector([1, 2, 3, 4, 5]);
}
public function testConstructorWithIterable () : void {
$iterable = new class implements IteratorAggregate {
public function getIterator () : Traversable {
yield 42;
yield 100;
}
};
$vector = new Vector($iterable);
$this->assertEquals([42, 100], $vector->toArray());
}
public function testFrom () : void {
$this->assertEquals([42, 100], Vector::from([42, 100])->toArray());
}
public function testGet () : void {
$vector = new Vector(["a", "b", "c"]);
$this->assertEquals("b", $vector->get(1));
}
public function testGetOverflow () : void {
$this->expectException(InvalidArgumentException::class);
$this->vector->get(800);
}
public function testGetOr () : void {
$vector = new Vector(["a", "b", "c"]);
$this->assertEquals("X", $vector->getOr(800, "X"));
}
public function testSet () : void {
$vector = new Vector(["a", "b", "c"]);
$vector->set(1, "x"); // should replace "b"
$this->assertEquals(["a", "x", "c"], $vector->toArray());
}
public function testContains () : void {
$this->assertTrue($this->vector->contains(2));
$this->assertFalse($this->vector->contains(666));
}
public function testCount () : void {
$this->assertEquals(5, $this->vector->count());
$this->assertEquals(0, (new Vector)->count());
}
public function testClear () : void {
$this->vector->clear();
$this->assertEquals(0, $this->vector->count());
}
public function testIsEmpty () : void {
$this->vector->clear();
$this->assertTrue($this->vector->isEmpty());
}
public function testPush () : void {
$this->vector->push(6);
$this->assertEquals([1, 2, 3, 4, 5, 6], $this->vector->toArray());
}
public function testAppend () : void {
$this->vector->append([6, 7, 8]);
$this->assertEquals([1, 2, 3, 4, 5, 6, 7 ,8], $this->vector->toArray());
}
public function testUpdate () : void {
$this->vector->update([5, 5, 5, 6, 7, 8]); // 5 already exists
$this->assertEquals([1, 2, 3, 4, 5, 6, 7 ,8], $this->vector->toArray());
}
public function testMap () : void {
$actual = $this->vector
->map(function ($x) { return $x * $x; })
->toArray();
$this->assertEquals([1, 4, 9, 16, 25], $actual);
}
public function testMapKeys () : void {
$vector = new Vector(["foo", "bar", "quux", "xizzy"]);
$filter = function ($key) {
return 0; // Let's collapse our array
};
$actual = $vector->mapKeys($filter)->toArray();
$this->assertEquals(["xizzy"], $actual);
}
+ public function testFlatMap () : void {
+ $expected = [
+ // Squares and cubes
+ 1, 1,
+ 4, 8,
+ 9, 27,
+ 16, 64,
+ 25, 125
+ ];
+
+ $callback = function ($n) {
+ yield $n * $n;
+ yield $n * $n * $n;
+ };
+
+ $actual = $this->vector->flatMap($callback)->toArray();
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testFlatMapWithKeyValueCallback() : void {
+ $vector = new Vector(["foo", "bar", "quux", "xizzy"]);
+
+ $callback = function (int $key, string $value) {
+ yield "$key::$value";
+ yield "$value ($key)";
+ };
+
+ $expected = [
+ "0::foo",
+ "foo (0)",
+
+ "1::bar",
+ "bar (1)",
+
+ "2::quux",
+ "quux (2)",
+
+ "3::xizzy",
+ "xizzy (3)",
+ ];
+
+ $actual = $vector->flatMap($callback)->toArray();
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testFlatMapWithCallbackWithoutArgument() : void {
+ $this->expectException(InvalidArgumentException::class);
+
+ $callback = function () {};
+ $this->vector->flatMap($callback);
+ }
+
public function testFilter () : void {
$vector = new Vector(["foo", "bar", "quux", "xizzy"]);
$filter = function ($item) {
return strlen($item) === 3; // Let's keep 3-letters words
};
$actual = $vector->filter($filter)->toArray();
$this->assertEquals(["foo", "bar"], $actual);
}
public function testFilterWithBadCallback () : void {
$this->expectException(InvalidArgumentException::class);
$badFilter = function () {};
$this->vector->filter($badFilter);
}
public function testFilterKeys () : void {
$filter = function ($key) {
return $key % 2 === 0; // Let's keep even indices
};
$actual = $this->vector
->filterKeys($filter)
->toArray();
$this->assertEquals([0, 2, 4], array_keys($actual));
}
public function testImplode() : void {
$actual = (new Vector(["a", "b", "c"]))
->implode(".")
->__toString();
$this->assertEquals("a.b.c", $actual);
}
public function testImplodeWithoutDelimiter() : void {
$actual = (new Vector(["a", "b", "c"]))
->implode("")
->__toString();
$this->assertEquals("abc", $actual);
}
public function testExplode() : void {
$actual = Vector::explode(".", "a.b.c");
$this->assertEquals(["a", "b", "c"], $actual->toArray());
}
public function testExplodeWithoutDelimiter() : void {
$actual = Vector::explode("", "a.b.c");
$this->assertEquals(["a.b.c"], $actual->toArray());
}
///
/// ArrayAccess
///
public function testArrayAccessFailsWithStringKey () : void {
$this->expectException(InvalidArgumentException::class);
$this->vector["foo"];
}
public function testOffsetExists () : void {
$this->assertTrue(isset($this->vector[0]));
$this->assertFalse(isset($this->vector[8]));
}
public function testOffsetSetWithoutOffset () : void {
$this->vector[] = 6;
$this->assertEquals(6, $this->vector[5]);
}
public function testOffsetSet () : void {
$this->vector[0] = 9;
$this->assertEquals(9, $this->vector[0]);
}
public function testOffsetUnset () : void {
unset($this->vector[2]);
$expected = [
0 => 1,
1 => 2,
// vector[2] has been unset
3 => 4,
4 => 5,
];
$this->assertEquals($expected, $this->vector->toArray());
}
///
/// IteratorAggregate
///
public function testGetIterator () : void {
$this->assertEquals([1, 2, 3, 4, 5], iterator_to_array($this->vector));
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 24, 19:31 (5 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2258769
Default Alt Text
(34 KB)
Attached To
Mode
rKERUALD Keruald libraries development repository
Attached
Detach File
Event Timeline
Log In to Comment