/** * Returns an infinite, traversable sequence that exponentially grows by given percentage * * @param integer $start * @param integer $percentage Integer between 1 and 100 * @return ExponentialSequence * @throws InvalidArgumentException */ function sequence_exponential($start, $percentage) { InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 1, __METHOD__, 1); InvalidArgumentException::assertIntegerGreaterThanOrEqual($percentage, 1, __METHOD__, 2); InvalidArgumentException::assertIntegerLessThanOrEqual($percentage, 100, __METHOD__, 2); return new ExponentialSequence($start, $percentage); }
/** * Retry a callback until the number of retries are reached or the callback does no longer throw an exception * * @param callable $callback * @param integer $retries * @param Traversable|null $delaySequence Default: no delay between calls * @throws Exception Any exception thrown by the callback * @throws InvalidArgumentException * @return mixed Return value of the function */ function retry(callable $callback, $retries, Traversable $delaySequence = null) { InvalidArgumentException::assertIntegerGreaterThanOrEqual($retries, 1, __FUNCTION__, 2); if ($delaySequence) { $delays = new AppendIterator(); $delays->append(new InfiniteIterator($delaySequence)); $delays->append(new InfiniteIterator(new ArrayIterator([0]))); $delays = new LimitIterator($delays, $retries); } else { $delays = array_fill_keys(range(0, $retries), 0); } $retry = 0; foreach ($delays as $delay) { try { return $callback($retry, $delay); } catch (Exception $e) { if ($retry === $retries - 1) { throw $e; } } if ($delay > 0) { usleep($delay); } ++$retry; } }
/** * Recombines arrays by index and applies a callback optionally * * @param $args array|Traversable $collection One or more callbacks * @return array */ function zip($arg) { $args = func_get_args(); $callback = null; if (is_callable(end($args))) { $callback = array_pop($args); } foreach ($args as $position => $arg) { InvalidArgumentException::assertCollection($arg, __FUNCTION__, $position + 1); } $result = []; foreach ((array) reset($args) as $index => $value) { $zipped = []; foreach ($args as $arg) { $zipped[] = isset($arg[$index]) ? $arg[$index] : null; } if ($callback !== null) { /** @var callable $callback */ // $zipped = $callback(...$zipped); $zipped = call_user_func_array($callback, $zipped); } $result[] = $zipped; } return $result; }
public function __construct($start, $amount) { InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 0, __METHOD__, 1); InvalidArgumentException::assertInteger($amount, __METHOD__, 2); $this->start = $start; $this->amount = $amount; }
/** * Returns a function that invokes method `$method` with arguments `$methodArguments` on the object * * @param string $methodName * @param array $arguments * @return callable */ function invoker($methodName, array $arguments = []) { InvalidArgumentException::assertMethodName($methodName, __FUNCTION__, 1); return static function ($object) use($methodName, $arguments) { return $object->{$methodName}(...$arguments); }; }
/** * Recombines arrays by index (column) and applies a callback optionally * * When the input collections are different lengths the resulting collections * will all have the length which is required to fit all the keys * * @param $args array|Traversable $collection One or more callbacks * @return array */ function zip_all(...$args) { /** @var callable|null $callback */ $callback = null; if (is_callable(end($args))) { $callback = array_pop($args); } foreach ($args as $position => $arg) { InvalidArgumentException::assertCollection($arg, __FUNCTION__, $position + 1); } $resultKeys = []; foreach ($args as $arg) { foreach ($arg as $index => $value) { $resultKeys[] = $index; } } $resultKeys = array_unique($resultKeys); $result = []; foreach ($resultKeys as $key) { $zipped = []; foreach ($args as $arg) { $zipped[] = isset($arg[$key]) ? $arg[$key] : null; } $result[$key] = $zipped; } if ($callback !== null) { foreach ($result as $key => $column) { $result[$key] = $callback(...$column); } } return $result; }
/** * Iterates over a collection of elements, yielding each in turn to a callback function. Each invocation of $callback * is called with three arguments: (element, index, collection) * * @param Traversable|array $collection * @param callable $callback * @return null */ function each($collection, callable $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $index => $element) { $callback($element, $index, $collection); } }
/** * Memoizes callbacks and returns their value instead of calling them * * @param callable $callback Callable closure or function * @param array $arguments Arguments * @param array|string $key Optional memoize key to override the auto calculated hash * @return mixed */ function memoize($callback, array $arguments = array(), $key = null) { static $storage = array(); if ($callback === null) { $storage = array(); return null; } InvalidArgumentException::assertCallback($callback, __FUNCTION__, 1); static $keyGenerator = null; if (!$keyGenerator) { $keyGenerator = function ($value) use(&$keyGenerator) { $type = gettype($value); if ($type === 'array') { $key = join(':', map($value, $keyGenerator)); } elseif ($type === 'object') { $key = get_class($value) . ':' . spl_object_hash($value); } else { $key = (string) $value; } return $key; }; } if ($key === null) { $key = $keyGenerator(array_merge(array($callback), $arguments)); } else { $key = $keyGenerator($key); } if (!isset($storage[$key]) && !array_key_exists($key, $storage)) { $storage[$key] = call_user_func_array($callback, $arguments); } return $storage[$key]; }
/** * Iterates over a collection of elements, yielding each in turn to a callback function. Each invocation of $callback * is called with three arguments: (element, index, collection) * * @param Traversable|array $collection * @param callable $callback * @return null */ function each($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); foreach ($collection as $index => $element) { call_user_func($callback, $element, $index, $collection); } }
public function testHashIterator() { $flat = flat_map(new ArrayIterator(['ka' => 'a', 'kb' => ['b'], 'kc' => ['C' => 'c'], 'kd' => [['d']], 'ke' => null, null]), function ($v, $k, $collection) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 3); return $v; }); $this->assertSame(['a', 'b', 'c', ['d']], $flat); }
/** * Alias for Functional\first * * @param Traversable|array $collection * @param callable $callback * @return mixed */ function head($collection, $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } return first($collection, $callback); }
/** * @param Traversable|array $collection * @param callable $callback * @param mixed $initial * @return array */ function reduce_left($collection, callable $callback, $initial = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $index => $value) { $initial = $callback($value, $index, $collection, $initial); } return $initial; }
public function __construct($start, $percentage) { InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 1, __METHOD__, 1); InvalidArgumentException::assertIntegerGreaterThanOrEqual($percentage, 1, __METHOD__, 2); InvalidArgumentException::assertIntegerLessThanOrEqual($percentage, 100, __METHOD__, 2); $this->start = $start; $this->percentage = $percentage; }
public function testDuplicateKeys() { $fn = function ($v, $k, $collection) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 3); return $k[0]; }; $this->assertSame(['k' => 'val2'], reindex($this->hash, $fn)); }
/** * Produces a new array of elements by mapping each element in collection through a transformation function (callback). * Callback arguments will be element, index, collection * * @param Traversable|array $collection * @param callable $callback * @return array */ function map($collection, callable $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $aggregation = []; foreach ($collection as $index => $element) { $aggregation[$index] = $callback($element, $index, $collection); } return $aggregation; }
/** * Returns true if all of the elements in the collection pass the callback falsy test. Opposite of Functional\all(). * Callback arguments will be element, index, collection. * * @param Traversable|array $collection * @param callable $callback * @return bool */ function none($collection, callable $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $index => $element) { if ($callback($element, $index, $collection)) { return false; } } return true; }
/** * Returns a function that expects an object as the first param and tries to invoke the given method on it * * @param string $methodName * @param array $arguments * @param mixed $defaultValue * @return callable */ function partial_method($methodName, array $arguments = [], $defaultValue = null) { InvalidArgumentException::assertMethodName($methodName, __FUNCTION__, 1); return function ($object) use($methodName, $arguments, $defaultValue) { if (!is_callable([$object, $methodName])) { return $defaultValue; } return $object->{$methodName}(...$arguments); }; }
/** * Takes a nested combination of collections and returns their contents as a single, flat array. * Does not preserve indexs. * * @param Traversable|array $collection * @return array */ function flatten($collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($collection)); $result = array(); foreach ($it as $val) { $result[] = $val; } return $result; }
/** * Partitions a collection by callback result. The truthy partition is the first one * (array index "0"), the falsy the second one (array index "1") * * @param Traversable|array $collection * @param callable $callback * @return array */ function partition($collection, callable $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $partitions = [0 => [], 1 => []]; foreach ($collection as $index => $element) { $partitionKey = $callback($element, $index, $collection) ? 0 : 1; $partitions[$partitionKey][$index] = $element; } return $partitions; }
/** * Produces a new array of elements by mapping each element in collection through a transformation function (callback). * Callback arguments will be element, index, collection * * @param Traversable|array $collection * @param callable $callback * @return array */ function map($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $aggregation = array(); foreach ($collection as $index => $element) { $aggregation[$index] = call_user_func($callback, $element, $index, $collection); } return $aggregation; }
/** * Returns true if the collection contains the given value. If the third parameter is * true values will be compared in strict mode * * @param Traversable|array $collection * @param mixed $value * @param bool $strict * @return bool */ function contains($collection, $value, $strict = true) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $element) { if ($value === $element || !$strict && $value == $element) { return true; } } return false; }
/** * Returns true if all elements of the collection are strictly true * * @param Traversable|array $collection * @return bool */ function true($collection) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $value) { if ($value !== true) { return false; } } return true; }
/** * Takes a collection and returns the quotient of all elements * * @param Traversable|array $collection * @param integer|float $initial * @return integer|float */ function ratio($collection, $initial = 1) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $result = $initial; foreach ($collection as $value) { if (is_numeric($value)) { $result /= $value; } } return $result; }
function test() { $fn = function ($v, $k, $collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); return $v > 2; }; $this->assertSame(array(2 => 3, 3 => 4), tail($this->array, $fn)); $this->assertSame(array(2 => 3, 3 => 4), tail($this->iterator, $fn)); $this->assertSame(array(), tail($this->badArray, $fn)); $this->assertSame(array(), tail($this->badIterator, $fn)); }
function test() { $fn = function ($v, $k, $collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 3); return is_int($k) ? $k % 2 == 0 : $v[3] % 2 == 0; }; $this->assertSame(array(array(0 => 'value1', 2 => 'value3'), array(1 => 'value2')), partition($this->array, $fn)); $this->assertSame(array(array(0 => 'value1', 2 => 'value3'), array(1 => 'value2')), partition($this->iterator, $fn)); $this->assertSame(array(array('k2' => 'val2'), array('k1' => 'val1', 'k3' => 'val3')), partition($this->hash, $fn)); $this->assertSame(array(array('k2' => 'val2'), array('k1' => 'val1', 'k3' => 'val3')), partition($this->hashIterator, $fn)); }
public function test() { $fn = function ($v, $k, $collection) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); return $v == 'first' && $k == 0 || $v == 'third' && $k == 2; }; $this->assertSame('third', last($this->list, $fn)); $this->assertSame('third', last($this->listIterator, $fn)); $this->assertNull(last($this->badArray, $fn)); $this->assertNull(last($this->badIterator, $fn)); }
/** * Invoke a callback on a value if the value is not null * * @param mixed $value * @param callable $callback * @return mixed */ function with($value, $callback) { InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); if ($value === null) { return null; } if (is_callable($value)) { $value = $value(); } return $callback($value); }
/** * @dataProvider getAliases */ function test($functionName) { $callback = function ($v, $k, $collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); return $v == 'second' && $k == 1; }; $this->assertSame('second', $functionName($this->array, $callback)); $this->assertSame('second', $functionName($this->iterator, $callback)); $this->assertNull($functionName($this->badArray, $callback)); $this->assertNull($functionName($this->badIterator, $callback)); }
public function test() { $fn = function ($v, $k, $collection) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 3); return is_int($k) ? $k % 2 == 0 : $v[3] % 2 == 0 ? 'foo' : ''; }; $this->assertSame(['foo' => [0 => 'value1', 2 => 'value3'], '' => [1 => 'value2', 3 => 'value4']], group($this->list, $fn)); $this->assertSame(['foo' => [0 => 'value1', 2 => 'value3'], '' => [1 => 'value2', 3 => 'value4']], group($this->listIterator, $fn)); $this->assertSame(['' => ['k1' => 'val1', 'k3' => 'val3'], 'foo' => ['k2' => 'val2']], group($this->hash, $fn)); $this->assertSame(['' => ['k1' => 'val1', 'k3' => 'val3'], 'foo' => ['k2' => 'val2']], group($this->hashIterator, $fn)); }
function test() { $fn = function ($v, $k, $collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 3); return $v == 'wrong' && strlen($k) > 0; }; $this->assertSame(array(0 => 'value', 2 => 'value'), reject($this->array, $fn)); $this->assertSame(array(0 => 'value', 2 => 'value'), reject($this->iterator, $fn)); $this->assertSame(array('k1' => 'value', 'k3' => 'value'), reject($this->hash, $fn)); $this->assertSame(array('k1' => 'value', 'k3' => 'value'), reject($this->hashIterator, $fn)); }