/** * 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]; }
/** * 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); }
/** * 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); } }
/** * @param Traversable|array $collection * @param callable $callback * @param mixed $initial * @return array */ function reduce_left($collection, $callback, $initial = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); 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) { 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; }
/** * 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, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); 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; }
/** * Returns true if every value in the collection passes the callback truthy test. Opposite of Functional\none(). * Callback arguments will be element, index, collection * * @param Traversable|array $collection * @param callable $callback * @return bool */ function every($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); foreach ($collection as $index => $element) { if (!call_user_func($callback, $element, $index, $collection)) { return false; } } return true; }
/** * 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); }
/** * Invoke a callback on a value if the value is not null * * @param mixed $value * @param callable $callback * @param bool $invokeValue Set to false to not invoke $value if it is a callable. Will be removed in 2.0 * @return mixed */ function with($value, callable $callback, $invokeValue = true) { InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); if ($value === null) { return null; } if ($invokeValue && is_callable($value)) { trigger_error('Invoking the value is deprecated and will be removed in 2.0', E_USER_DEPRECATED); $value = $value(); } return $callback($value); }
/** * @param Traversable|array $collection * @param callable $callback * @param mixed $initial * @return array */ function reduce_right($collection, $callback, $initial = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); 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; }
/** * 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) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { 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; }
/** * Drop all elements from a collection after callback returns true * * @param Traversable|array $collection * @param callable|integer $callback * @return array */ function drop_last($collection, callable $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $result = []; $drop = false; foreach ($collection as $index => $element) { if (!$drop && !$callback($element, $index, $collection)) { break; } $result[$index] = $element; } return $result; }
/** * Groups a collection by index returned by callback. * * @param Traversable|array $collection * @param callable $callback * @return array */ function group($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $groups = array(); foreach ($collection as $index => $element) { $groupKey = call_user_func($callback, $element, $index, $collection); InvalidArgumentException::assertValidArrayKey($groupKey, __FUNCTION__); if (!isset($groups[$groupKey])) { $groups[$groupKey] = array(); } $groups[$groupKey][$index] = $element; } return $groups; }
/** * 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) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { 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; } } }
/** * Sorts a collection with a user-defined function, optionally preserving array keys * * @param Traversable|array $collection * @param callable $callback * @param bool $preserveKeys * @return array */ function sort($collection, $callback, $preserveKeys = false) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); InvalidArgumentException::assertBoolean($preserveKeys, __FUNCTION__, 3); if ($collection instanceof \Traversable) { $array = iterator_to_array($collection); } else { $array = $collection; } $fn = $preserveKeys ? 'uasort' : 'usort'; $fn($array, function ($left, $right) use($callback, $collection) { return call_user_func($callback, $left, $right, $collection); }); return $array; }
/** * Pick a single element from a collection of objects or arrays by index. * If no such index exists, return the default value. * * @param ArrayAccess|array $collection * @param mixed $index * @param mixed $default * @param callable $callback Custom function to check if index exists, default function is "isset" * @return mixed */ function pick($collection, $index, $default = null, $callback = null) { InvalidArgumentException::assertArrayAccess($collection, __FUNCTION__, 1); if ($callback !== null) { InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); } if ($callback === null) { if (!isset($collection[$index])) { return $default; } } else { if (!call_user_func($callback, $collection, $index)) { return $default; } } return $collection[$index]; }
/** * Drop all elements from a collection until callback returns false * * @param Traversable|array $collection * @param callable $callback * @return array */ function drop_first($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $result = array(); $drop = true; foreach ($collection as $index => $element) { if ($drop) { if (call_user_func($callback, $element, $index, $collection)) { continue; } $drop = false; } $result[$index] = $element; } return $result; }
/** * flat_map works applying a function (callback) that returns a sequence for each element in a collection, * and flattening the results into the resulting array. * * flat_map(...) differs from flatten(map(...)) because it only flattens one level of nesting, * whereas flatten will recursively flatten nested collections. * * For example if map(collection, callback) returns [[],1,[2,3],[[4]]] * then flat_map(collection, callback) will return [1,2,3,[4]] * while flatten(map(collection, callback)) will return [1,2,3,4] * * @param Traversable|array $collection * @param callable $callback * @return array */ function flat_map($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); $flattened = array(); foreach ($collection as $index => $element) { $result = call_user_func($callback, $element, $index, $collection); if (is_array($result) || $result instanceof Traversable) { foreach ($result as $item) { $flattened[] = $item; } } else { $flattened[] = $result; } } return $flattened; }
/** * Returns all items from $collection except first element (head). Preserves $collection keys. * Takes an optional callback for filtering the collection. * * @param Traversable|array $collection * @param callable $callback * @return array */ function tail($collection, $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback !== null) { 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 an array of unique elements * * @param Traversable|array $collection * @param callable $callback * @param bool $strict * @return array */ function unique($collection, $callback = null, $strict = true) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); if ($callback != null) { 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; }
/** * Alias of Functional\select() * * @param Traversable|array $collection * @param callable $callback * @return array */ function filter($collection, $callback) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2); return select($collection, $callback); }