/** * Memoizes callbacks and returns there 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) { Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 1); static $keyGenerator = null, $storage = array(); 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]; }
/** * Alias for Functional\first * * @param Traversable|array $collection * @param callable $callback * @return mixed */ function head($collection, $callback = null) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } return first($collection, $callback); }
/** * 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) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); foreach ($collection as $index => $element) { call_user_func($callback, $element, $index, $collection); } }
/** * @param Traversable|array $collection * @param callable $callback * @return array */ function reduce_left($collection, $callback, $initial = null) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); foreach ($collection as $index => $value) { $initial = call_user_func($callback, $value, $index, $collection, $initial); } return $initial; }
/** * 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) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\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 all elements of the collection evaluate to true * * @param Traversable|array $collection * @return bool */ function truthy($collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $value) { if (!$value) { return false; } } return true; }
/** * Returns the first index holding specified value in the ccollection. Returns false if value was not found * * @param Traversable|array $collection * @param mixed $value * @return mixed */ function first_index_of($collection, $value) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $index => $element) { if ($element === $value) { return $index; } } return false; }
/** * 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) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); foreach ($collection as $element) { if ($value === $element || !$strict && $value == $element) { return true; } } return false; }
/** * Returns the last index holding specified value in the ccollection. Returns false if value was not found * * @param Traversable|array $collection * @param mixed $value * @return mixed */ function last_index_of($collection, $value) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $matchingIndex = false; foreach ($collection as $index => $element) { if ($element === $value) { $matchingIndex = $index; } } return $matchingIndex; }
/** * 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, $callback) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); foreach ($collection as $index => $element) { if (call_user_func($callback, $element, $index, $collection)) { return false; } } return true; }
/** * Partitions a collection by callback result. The thruthy 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, $callback) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $partitions = array(0 => array(), 1 => array()); foreach ($collection as $index => $element) { $partitionKey = call_user_func($callback, $element, $index, $collection) ? 0 : 1; $partitions[$partitionKey][$index] = $element; } return $partitions; }
/** * Takes a collection and returns the difference of all elements * * @param Traversable|array $collection * @return integer|float */ function difference($collection, $initial = 0) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $result = $initial; foreach ($collection as $value) { if (is_numeric($value)) { $result -= $value; } } return $result; }
/** * @param Traversable|array $collection * @param callable $callback * @return array */ function reduce_right($collection, $callback, $initial = null) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $data = array(); foreach ($collection as $index => $value) { $data[] = array($index, $value); } while (list($index, $value) = array_pop($data)) { $initial = call_user_func($callback, $value, $index, $collection, $initial); } return $initial; }
/** * Calls the method named by $methodName on first value in the collection. Any extra arguments passed to invoke will be * forwarded on to the method invocation. * * @param Traversable|array $collection * @param string $methodName * @param array $arguments * @return array */ function invoke_first($collection, $methodName, array $arguments = array()) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertMethodName($methodName, __FUNCTION__, 2); foreach ($collection as $index => $element) { $value = null; $callback = array($element, $methodName); if (is_callable($callback)) { return call_user_func_array($callback, $arguments); } } return null; }
/** * Returns the minimum value of a collection * * @param Traversable|array $collection * @param callable $callback * @return array */ function minimum($collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $max = null; foreach ($collection as $index => $element) { if (!is_numeric($element)) { continue; } if ($element < $max || $max === null) { $max = $element; } } return $max; }
/** * Drop all elements from a collection after callback returns true * * @param Traversable|array $collection * @param callable|integer $callback * @return array */ function drop_last($collection, $callback) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $result = array(); $drop = false; foreach ($collection as $index => $element) { if (!$drop && !call_user_func($callback, $element, $index, $collection)) { break; } $result[$index] = $element; } return $result; }
/** * Looks through each element in the collection, returning the last one that passes a truthy test (callback). * Callback arguments will be element, index, collection * * @param Traversable|array $collection * @param callable $callback * @return mixed */ function last($collection, $callback = null) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } $match = null; foreach ($collection as $index => $element) { if ($callback === null || call_user_func($callback, $element, $index, $collection)) { $match = $element; } } return $match; }
/** * Looks through each element in the collection, returning the first one that passes a truthy test (callback). The * function returns as soon as it finds an acceptable element, and doesn't traverse the entire collection. Callback * arguments will be element, index, collection * * @param Traversable|array $collection * @param callable $callback * @return mixed */ function first($collection, $callback = null) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } foreach ($collection as $index => $element) { if ($callback === null) { return $element; } if (call_user_func($callback, $element, $index, $collection)) { return $element; } } }
/** * Groups a collection by index returned by callback. * * @param Traversable|array $collection * @param callable $callback * @return array */ function group($collection, $callback) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $groups = array(); foreach ($collection as $index => $element) { $groupKey = call_user_func($callback, $element, $index, $collection); Exceptions\InvalidArgumentException::assertValidArrayKey($groupKey, __FUNCTION__); if (!isset($groups[$groupKey])) { $groups[$groupKey] = array(); } $groups[$groupKey][$index] = $element; } return $groups; }
/** * Returns the average of all numeric values in the array or null if no numeric value was found * * @param Traversable|array $collection * @return null|float|int */ function average($collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); $sum = null; $divisor = 0; foreach ($collection as $element) { if (is_numeric($element)) { $sum += $element; ++$divisor; } } if ($sum === null) { return null; } return $sum / $divisor; }
/** * Drop all elements from a collection until callback returns false * * @param Traversable|array $collection * @param callable $callback * @return array */ function drop_first($collection, $callback) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $result = array(); $drop = true; foreach ($collection as $index => $element) { if ($drop) { if (!call_user_func($callback, $element, $index, $collection)) { $drop = false; } else { continue; } } $result[$index] = $element; } return $result; }
/** * Returns all items from $collection except first element (head). Preserves $collection keys. * Takes an optional callback for filtering the collection. * * @param Traversable|array $ * @param callable $callback * @return array */ function tail($collection, $callback = null) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } $tail = array(); $isHead = true; foreach ($collection as $index => $element) { if ($isHead) { $isHead = false; continue; } if (!$callback || call_user_func($callback, $element, $index, $collection)) { $tail[$index] = $element; } } return $tail; }
/** * Returns a array of unique elements * * @param Traversable|array $collection * @param callable $callback * @return array */ function unique($collection, $callback = null, $strict = true) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback != null) { Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } $indexes = array(); $aggregation = array(); foreach ($collection as $key => $element) { if ($callback) { $index = call_user_func($callback, $element, $key, $collection); } else { $index = $element; } if (!in_array($index, $indexes, $strict)) { $aggregation[$key] = $element; $indexes[] = $index; } } return $aggregation; }
/** * Recombines arrays by index and applies a callback optionally * * @param Traversable|array $collection One or more callbacks * @param callable $callback Optionally the last argument can be a callback * @return array */ function zip($collection) { $args = func_get_args(); $callback = null; if (is_callable(end($args))) { $callback = array_pop($args); } foreach ($args as $position => $collection) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, $position + 1); } $result = array(); foreach (func_get_arg(0) as $index => $value) { $zipped = array(); foreach ($args as $arg) { $zipped[] = isset($arg[$index]) ? $arg[$index] : null; } if ($callback !== null) { $zipped = call_user_func_array($callback, $zipped); } $result[] = $zipped; } return $result; }
/** * Alias of Functional\select() * * @param Traversable|array $collection * @param callable $callback * @return array */ function filter($collection, $callback) { Exceptions\InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); Exceptions\InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); return select($collection, $callback); }