diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index b626716..1d16076 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -1,12 +1,16 @@ name: bingo-functional CI -on: [push, pull_request] +on: + push: + branches: + - v1.x + - v2.x jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] name: PHP ${{ matrix.php }} steps: - uses: actions/checkout@v3 diff --git a/changes.md b/changes.md index 877b21a..967c138 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,32 @@ # bingo-functional changes +## v2.5.0 + +- Added `Chemem\Bingo\Functional\listFromPaths` +- Added nullable type definitions to arguments in the following functions + - `Chemem\Bingo\Functional\paths` + - `Chemem\Bingo\Functional\toException` + - `Chemem\Bingo\Functional\Functors\Monads\bind` +- Reworked the internals of the following functions + - `Chemem\Bingo\Functional\memoize` + - `Chemem\Bingo\Functional\head` + - `Chemem\Bingo\Functional\last` + - `Chemem\Bingo\Functional\indexOf` + - `Chemem\Bingo\Functional\intersects` + - `Chemem\Bingo\Functional\isArrayOf` + - `Chemem\Bingo\Functional\omit` + - `Chemem\Bingo\Functional\where` + - `Chemem\Bingo\Functional\keysExist` + - `Chemem\Bingo\Functional\any` + - `Chemem\Bingo\Functional\assocPath` + - `Chemem\Bingo\Functional\every` + - `Chemem\Bingo\Functional\fromPairs` + - `Chemem\Bingo\Functional\has` + - `Chemem\Bingo\Functional\tail` +- Improved iteration patterns in functions subsumed in the following classes + - `Chemem\Bingo\Functional\Immutable\Collection` + - `Chemem\Bingo\Functional\Immutable\Tuple` + ## v2.4.0 - Added `jsonDecode` and `extract` functions diff --git a/composer.json b/composer.json index ec5ab70..80418cf 100644 --- a/composer.json +++ b/composer.json @@ -68,7 +68,7 @@ } ], "require": { - "php": "^7 || ^8" + "php": "^7.2 || ^8" }, "require-dev": { "ergebnis/composer-normalize": "^2", diff --git a/src/Functional/Any.php b/src/Functional/Any.php index 300de4b..4739256 100644 --- a/src/Functional/Any.php +++ b/src/Functional/Any.php @@ -28,15 +28,14 @@ */ function any($list, callable $func): bool { - return fold( - function (bool $valid, $entry) use ($func) { - if ($func($entry)) { - return true; - } + $valid = false; - return $valid; - }, - $list, - false - ); + foreach ($list as $value) { + if ($func($value)) { + $valid = true; + break; + } + } + + return $valid; } diff --git a/src/Functional/ArrayKeysExist.php b/src/Functional/ArrayKeysExist.php index c26c8a4..7f9e70e 100644 --- a/src/Functional/ArrayKeysExist.php +++ b/src/Functional/ArrayKeysExist.php @@ -49,17 +49,31 @@ function arrayKeysExist(array $list, ...$keys): bool */ function keysExist($list, ...$keys): bool { - $check = fold( - function ($acc, $val, $idx) use ($keys) { - if (\in_array($idx, $keys)) { - $acc[] = $val; + $exist = false; + $matches = 0; + $idx = 0; + + while (isset($keys[$idx])) { + $next = $keys[$idx]; + + if (\is_object($list)) { + if (isset($list->{$next})) { + $matches += 1; } + } + + if (\is_array($list)) { + if (isset($list[$next])) { + $matches += 1; + } + } + + if (!isset($keys[$idx + 1]) && equals($matches, $idx + 1)) { + $exist = true; + } - return $acc; - }, - $list, - [] - ); + $idx++; + } - return equals(size($check), size($keys)); + return $exist; } diff --git a/src/Functional/AssocPath.php b/src/Functional/AssocPath.php index d5f560d..cbea54e 100644 --- a/src/Functional/AssocPath.php +++ b/src/Functional/AssocPath.php @@ -12,10 +12,8 @@ namespace Chemem\Bingo\Functional; require_once __DIR__ . '/Internal/_JsonPath.php'; -require_once __DIR__ . '/Internal/_MergeN.php'; use function Chemem\Bingo\Functional\Internal\_jsonPath; -use function Chemem\Bingo\Functional\Internal\_mergeN; const assocPath = __NAMESPACE__ . '\\assocPath'; @@ -36,78 +34,30 @@ */ function assocPath($path, $val, $list) { - $path = _jsonPath($path); + $keys = _jsonPath($path); + $tmp = &$list; + $idx = 0; - if (!\is_null(pluckPath($path, $list))) { - $pathc = size($path); + while ($keys) { + $key = \array_shift($keys); - if (equals($pathc, 0)) { - return $list; - } - - $idx = head($path); - - if ($pathc > 1) { - $next = pluck($list, $idx); - - if (\is_object($next) || \is_array($next)) { - $val = assocPath(dropLeft($path), $val, $next); + if (\is_array($list)) { + if (!\is_array($tmp)) { + $tmp = []; } - } - - return assoc($idx, $val, $list); - } else { - $listc = size($list); - $lcheck = pluckPath(dropRight($path, 1), $list); - $clone = function ($path, $val, $list) use ( - &$clone, - $listc, - $lcheck - ) { - return fold( - function ($acc, $entry) use ( - &$clone, - $lcheck, - $list, - $listc, - $path, - $val - ) { - if (equals(size($acc), 1)) { - // preempt extraneous list concatenation - return $acc; - } - - if (equals(size($path), 1)) { - // check if the key exists - if (!\is_null($lcheck)) { - if (\is_array($lcheck)) { - $lcheck[$entry] = $val; - } elseif (\is_object($lcheck)) { - $lcheck->{$entry} = $val; - } - $acc = (array) $lcheck; - } else { - $acc[$entry] = $val; - } - - $acc[$entry] = $val; - } else { - $acc[$entry] = $clone(dropLeft($path), $val, $list); - } - - return $acc; - }, - $path, - [] - ); - }; - - $result = $clone($path, $val, $list); + $tmp = &$tmp[$key]; + } elseif (\is_object($list)) { + if (!\is_object($tmp)) { + $tmp = (new \ReflectionClass($list)) + ->newInstanceWithoutConstructor(); + } - return _mergeN($list, $result); + $tmp = &$tmp->{$key}; + } } + $tmp = $val; + return $list; } diff --git a/src/Functional/Every.php b/src/Functional/Every.php index 9febf9b..798d045 100644 --- a/src/Functional/Every.php +++ b/src/Functional/Every.php @@ -28,18 +28,14 @@ */ function every($list, callable $func): bool { - [$filterCount, $valCount] = fold( - function ($acc, $val) use ($func) { - [$fst, $snd] = $acc; - $fst = $fst + 1; + $valid = true; - $snd += $func($val) ? 1 : 0; + foreach ($list as $value) { + if (equals($func($value), false)) { + $valid = false; + break; + } + } - return [$fst, $snd]; - }, - $list, - [0, 0] - ); - - return equals($filterCount, $valCount); + return $valid; } diff --git a/src/Functional/FromPairs.php b/src/Functional/FromPairs.php index fa45cc8..3448703 100644 --- a/src/Functional/FromPairs.php +++ b/src/Functional/FromPairs.php @@ -29,13 +29,26 @@ function fromPairs($list) { return fold( function ($acc, $val, $key) { - if (equals(size($val), 2)) { - if (\is_object($acc)) { - $acc->{head($val)} = last($val); - unset($acc->{$key}); - } elseif (\is_array($acc)) { - $acc[head($val)] = last($val); - unset($acc[$key]); + if (\is_array($val) || \is_object($val)) { + $list = []; + + foreach ($val as $entry) { + $list[] = $entry; + + if (equals(++$count, 2)) { + $idx = pluck($list, 0); + $next = pluck($list, 1); + + if (\is_object($acc)) { + $acc->{$idx} = $next; + unset($acc->{$key}); + } elseif (\is_array($acc)) { + $acc[$idx] = $next; + unset($acc[$key]); + } + + break; + } } } diff --git a/src/Functional/Has.php b/src/Functional/Has.php index 379aa6e..03f48f8 100644 --- a/src/Functional/Has.php +++ b/src/Functional/Has.php @@ -28,19 +28,18 @@ */ function has($haystack, $needle): bool { - return fold( - function (bool $exists, $entry) use ($needle) { - if (equals($needle, $entry, true)) { - return true; - } + $exists = false; - if (\is_object($entry) || \is_array($entry)) { - $exists = has($entry, $needle); - } + foreach ($haystack as $entry) { + if (equals($needle, $entry, true)) { + $exists = true; + break; + } - return $exists; - }, - $haystack, - false - ); + if (\is_object($entry) || \is_array($entry)) { + $exists = has($entry, $needle); + } + } + + return $exists; } diff --git a/src/Functional/Head.php b/src/Functional/Head.php index 119c13a..1ef6609 100644 --- a/src/Functional/Head.php +++ b/src/Functional/Head.php @@ -16,30 +16,32 @@ * head * Outputs the first element in a list * - * head :: [a] -> a + * head :: [a] -> a -> a * * @param object|array $list + * @param mixed $default * @return mixed * @example * * head(range(4, 7)) * => 4 */ -function head($list, $def = null) +function head($list, $default = null) { - $count = 0; - - foreach ($list as $val) { - if (equals($count, 0)) { - return $val; - } + if ( + !( + \is_object($list) || + \is_array($list) + ) + ) { + return $default; + } - if ($count > 0) { - break; - } + \reset($list); - $count += 1; - } + $result = \current($list); - return $def; + return equals($result, false) ? + $default : + $result; } diff --git a/src/Functional/IndexOf.php b/src/Functional/IndexOf.php index 0b08b67..56d3a2a 100644 --- a/src/Functional/IndexOf.php +++ b/src/Functional/IndexOf.php @@ -28,15 +28,14 @@ */ function indexOf($list, $value, $default = null) { - return fold( - function ($acc, $entry, $idx) use ($value) { - if (equals($entry, $value, true)) { - $acc = $idx; - } + $result = $default; - return $acc; - }, - $list, - $default - ); + foreach ($list as $key => $entry) { + if (equals($value, $entry, true)) { + $result = $key; + break; + } + } + + return $result; } diff --git a/src/Functional/Internal/_Drop.php b/src/Functional/Internal/_Drop.php index 55f76fb..efd0ebb 100644 --- a/src/Functional/Internal/_Drop.php +++ b/src/Functional/Internal/_Drop.php @@ -29,37 +29,40 @@ */ function _drop($list, $count, $left = true) { - $listCount = _size($list); - ['result' => $result] = _fold( - function (array $acc, $val, $key) use ($count, $list, $left, $listCount) { - if ($left) { - if (($acc['count'] + 1) <= $count) { - if (\is_object($acc['result'])) { - unset($acc['result']->{$key}); - } elseif (\is_array($acc['result'])) { - unset($acc['result'][$key]); - } + $idx = 0; + + if ($left) { + foreach ($list as $key => $value) { + if (++$idx <= $count) { + if (\is_object($list)) { + unset($list->{$key}); + } elseif (\is_array($list)) { + unset($list[$key]); } } else { - if (($listCount - $acc['count']) <= $count) { - if (\is_object($list)) { - unset($acc['result']->{$key}); - } elseif (\is_array($acc['result'])) { - unset($acc['result'][$key]); - } - } + break; + } + } + } else { + \end($list); + + while ($idx < $count) { + $key = \key($list); + if (\is_object($list)) { + unset($list->{$key}); + } elseif (\is_array($list)) { + unset($list[$key]); } - $acc['count'] += 1; + $prev = \prev($list); + + if (!$prev) { + \end($list); + } - return $acc; - }, - $list, - [ - 'result' => $list, - 'count' => 0, - ] - ); + $idx++; + } + } - return $result; + return $list; } diff --git a/src/Functional/Internal/_JsonPath.php b/src/Functional/Internal/_JsonPath.php index 75029f6..0502341 100644 --- a/src/Functional/Internal/_JsonPath.php +++ b/src/Functional/Internal/_JsonPath.php @@ -20,19 +20,29 @@ * * @internal * @param array|string $path - * @return array + * @return mixed */ -function _jsonPath($path) +function _jsonPath($path, string $separator = '.') { - if ( - \is_string($path) && - \preg_match('/^(.){1,}|(\[\d\]){1,}$/i', $path) - ) { - return \explode( - '.', - \str_replace(['[', ']'], ['.', ''], $path) - ); - } - - return $path; + return \is_string($path) ? + \preg_split( + \sprintf( + '/(%s){1}/', + \preg_quote($separator, '/') + ), + \preg_replace( + [ + '/(\[){1}/', + '/(\]){1}/', + '/(\"){1}/' + ], + [ + $separator, + '', + '', + ], + $path + ) + ) : + $path; } diff --git a/src/Functional/Internal/_Partial.php b/src/Functional/Internal/_Partial.php index c660678..0003f6f 100644 --- a/src/Functional/Internal/_Partial.php +++ b/src/Functional/Internal/_Partial.php @@ -10,8 +10,7 @@ namespace Chemem\Bingo\Functional\Internal; -require_once __DIR__ . '/_Merge.php'; -require_once __DIR__ . '/_Size.php'; +require_once __DIR__ . '/_Fold.php'; const _partial = __NAMESPACE__ . '\\_partial'; @@ -34,20 +33,54 @@ function _partial( ): callable { $argCount = (new \ReflectionFunction($func)) ->getNumberOfRequiredParameters(); + $merge = function (...$item): array { + return _fold( + function (array $acc, $value) { + if (\is_array($value)) { + foreach ($value as $entry) { + $acc['size'] += 1; + $acc['acc'][] = $entry; + } + } - $acc = function (...$inner) use (&$acc, $func, $argCount, $left) { + return $acc; + }, + $item, + [ + 'size' => 0, + 'acc' => [], + ] + ); + }; + $acc = function (...$inner) use ( + &$acc, + $func, + $argCount, + $left, + $merge + ): callable { return function (...$innermost) use ( $inner, $acc, $func, $left, - $argCount + $argCount, + $merge ) { - $final = $left ? - _merge(false, $inner, $innermost) : - _merge(false, \array_reverse($innermost), \array_reverse($inner)); + [ + 'size' => $size, + 'acc' => $final + ] = $merge( + false, + $left ? + $inner : + \array_reverse($innermost), + $left ? + $innermost : + \array_reverse($inner) + ); - if ($argCount <= _size($final)) { + if ($argCount <= $size) { return $func(...$final); } diff --git a/src/Functional/Internal/_Props.php b/src/Functional/Internal/_Props.php index a256301..efb27bf 100644 --- a/src/Functional/Internal/_Props.php +++ b/src/Functional/Internal/_Props.php @@ -20,14 +20,14 @@ * * @internal * @param mixed $object - * @param Reflector $ref + * @param Reflector|null $ref * @param boolean $recurse * @return array */ function _props( $object, - \Reflector $ref = null, - bool $recurse = false + ?\Reflector $ref = null, + bool $recurse = false ): array { if (!\is_object($object)) { return $object; @@ -87,7 +87,13 @@ function (array $acc, $prop, $key) use ($object, $recurse) { return $acc; }, - !empty($props) ? $props : \get_object_vars($object), + !empty($props) ? + $props : + ( + PHP_VERSION_ID > 70400 ? + \get_mangled_object_vars($object) : + \get_object_vars($object) + ), [] ); } diff --git a/src/Functional/Intersects.php b/src/Functional/Intersects.php index 3f11ceb..77890fb 100644 --- a/src/Functional/Intersects.php +++ b/src/Functional/Intersects.php @@ -28,36 +28,14 @@ */ function intersects($first, $second): bool { - if (\is_object($first) && \is_object($second)) { - $first = \get_object_vars($first); - $second = \get_object_vars($second); - } - - $fsize = size($first); - $ssize = size($second); - $intersect = false; - - if ($fsize > $ssize) { - foreach ($second as $val) { - if (has($first, $val)) { - $intersect = true; - - if ($intersect == true) { - break; - } - } - } - } else { - foreach ($first as $val) { - if (has($second, $val)) { - $intersect = true; + $intersects = false; - if ($intersect == true) { - break; - } - } + foreach ($first as $value) { + if (has($second, $value)) { + $intersects = true; + break; } } - return $intersect; + return $intersects; } diff --git a/src/Functional/IsArrayOf.php b/src/Functional/IsArrayOf.php index 2bf38b6..dfa1e17 100644 --- a/src/Functional/IsArrayOf.php +++ b/src/Functional/IsArrayOf.php @@ -18,24 +18,34 @@ * * isArrayOf :: [a] -> String * - * @param array $list - * @return string + * @param array|object $list + * @return string|null * @example * * isArrayOf(['foo', 'bar', 'baz']) * => 'string' */ -function isArrayOf($list): string +function isArrayOf($list): ?string { - $types = fold( - function ($types, $entry) { - $types[] = \gettype($entry); + $cache = []; + $type = null; + $idx = 0; - return unique($types); - }, - $list, - [] - ); + foreach ($list as $value) { + $tmp = \gettype($value); - return size($types) > 1 ? 'mixed' : head($types); + if ($idx++ > 0) { + if (\in_array($tmp, $cache)) { + $type = $tmp; + } else { + $type = 'mixed'; + break; + } + } else { + $cache[] = $tmp; + $type = $tmp; + } + } + + return $type; } diff --git a/src/Functional/Last.php b/src/Functional/Last.php index aae528a..e8bc36b 100644 --- a/src/Functional/Last.php +++ b/src/Functional/Last.php @@ -16,7 +16,7 @@ * last * Outputs the last element in a list * - * last :: [a] -> a + * last :: [a] -> a -> a * * @param object|array $list * @return mixed @@ -27,18 +27,20 @@ */ function last($list, $default = null) { - $count = 0; - $size = size($list); + if ( + !( + \is_object($list) || + \is_array($list) + ) + ) { + return $default; + } - return fold( - function ($acc, $item) use (&$count, $size) { - if (equals($count, $size - 1)) { - return $item; - } + \end($list); - $count++; - }, - $list, - $default - ); + $result = \current($list); + + return equals($result, false) ? + $default : + $result; } diff --git a/src/Functional/ListFromPaths.php b/src/Functional/ListFromPaths.php new file mode 100644 index 0000000..709f007 --- /dev/null +++ b/src/Functional/ListFromPaths.php @@ -0,0 +1,52 @@ + Sum Array Object + * + * @param array|object $list + * @param string $separator + * @return array|object + * @example + * + * listFromPaths((object) ['foo.bar' => 12, 'foo.baz.qux' => 'foo']) + * => (object) ['foo' => ['bar' => 12, 'baz' => ['qux' => 'foo']]] + */ +function listFromPaths($list, string $separator = '.') +{ + return fold( + function ( + $acc, + $value, + string $path + ) use ($separator) { + $acc = assocPath( + _jsonPath($path, $separator), + $value, + $acc + ); + + return $acc; + }, + $list, + [] + ); +} diff --git a/src/Functional/Memoize.php b/src/Functional/Memoize.php index fd32f66..6b92d29 100644 --- a/src/Functional/Memoize.php +++ b/src/Functional/Memoize.php @@ -28,26 +28,46 @@ * memoize($fact)(5); * => 120 */ -function memoize(callable $function, bool $apc = false): callable +function memoize(callable $function, bool $apc = false) { - return function (...$args) use ($apc, $function) { - static $cache = []; - $key = \md5( - \extension_loaded('igbinary') ? - \igbinary_serialize($args) : - \serialize($args) - ); - - if ($apc && \extension_loaded('apcu')) { - \apcu_add($key, $function(...$args)); - - return \apcu_exists($key) ? \apcu_fetch($key) : $function(...$args); - } + return new class ($function, $apc) { + private $function; + private $apc; - if (!isset($cache[$key])) { - $cache[$key] = $function(...$args); + public function __construct(callable $function, bool $apc) + { + $this->function = $function; + $this->apc = $apc; } - return $cache[$key] ?? $function(...$args); + public function __invoke(...$args) + { + $key = \md5( + \extension_loaded('igbinary') ? + \igbinary_serialize($args) : + \serialize($args) + ); + + if ($this->apc && \extension_loaded('apcu')) { + $result = \apcu_fetch($key, $exists); + + if ($exists) { + return $result; + } + + $ret = ($this->function)(...$args); + \apcu_store($key, $ret); + + return $ret; + } + + if (isset($GLOBALS[$key])) { + return $GLOBALS[$key]; + } + + $GLOBALS[$key] = ($this->function)(...$args); + + return $GLOBALS[$key]; + } }; } diff --git a/src/Functional/Omit.php b/src/Functional/Omit.php index 4df8dc2..4bc823c 100644 --- a/src/Functional/Omit.php +++ b/src/Functional/Omit.php @@ -28,19 +28,23 @@ */ function omit($list, ...$keys) { - return fold( - function ($acc, $val, $idx) use ($keys) { - if (has($keys, $idx)) { - if (\is_object($acc)) { - unset($acc->{$idx}); - } elseif (\is_array($acc)) { - unset($acc[$idx]); - } + $idx = 0; + + while (isset($keys[$idx])) { + $key = $keys[$idx]; + + if (\is_object($list)) { + if (isset($list->{$key})) { + unset($list->{$key}); } + } elseif (\is_array($list)) { + if (isset($list[$key])) { + unset($list[$key]); + } + } + + $idx++; + } - return $acc; - }, - $list, - $list - ); + return $list; } diff --git a/src/Functional/Paths.php b/src/Functional/Paths.php index dfd2740..def751e 100644 --- a/src/Functional/Paths.php +++ b/src/Functional/Paths.php @@ -20,7 +20,7 @@ * * @param iterable $list * @param string $glue - * @param callable $modify + * @param callable|null $modify * @param string $prefix * @return iterable * @example @@ -31,7 +31,7 @@ function paths( $list, string $glue = '.', - callable $modify = null, + ?callable $modify = null, string $prefix = '' ): array { return fold( diff --git a/src/Functional/Tail.php b/src/Functional/Tail.php index 478551a..19fef20 100644 --- a/src/Functional/Tail.php +++ b/src/Functional/Tail.php @@ -27,22 +27,5 @@ */ function tail($list) { - [, $final] = fold(function ($acc, $val) { - [$count, $lst] = $acc; - $count = $count + 1; - - if ($count < 2) { - if (\is_object($lst)) { - unset($lst->{indexOf($lst, $val)}); - } else { - if (\is_array($lst)) { - unset($lst[indexOf($lst, $val)]); - } - } - } - - return [$count, $lst]; - }, $list, [0, $list]); - - return $final; + return dropLeft($list, 1); } diff --git a/src/Functional/ToException.php b/src/Functional/ToException.php index 3ad95b1..5670c7f 100644 --- a/src/Functional/ToException.php +++ b/src/Functional/ToException.php @@ -16,10 +16,10 @@ * toException * enables anomalous situation handling via function application * - * toException :: (a -> b) -> (Throwable -> c) -> b + * toException :: (a -> b) -> (Object -> b) -> b * * @param callable $func - * @param callable $handler + * @param callable|null $handler * @return callable * @example * @@ -31,7 +31,7 @@ * }, fn ($_) => INF)(3, 0) * => INF */ -function toException(callable $func, callable $handler = null): callable +function toException(callable $func, ?callable $handler = null): callable { return function (...$args) use ($func, $handler) { try { diff --git a/src/Functional/Where.php b/src/Functional/Where.php index fb6046d..b8da262 100644 --- a/src/Functional/Where.php +++ b/src/Functional/Where.php @@ -14,13 +14,12 @@ /** * where - * searches a multi-dimensional array using a fragment of a sub-array defined in the - * said composite + * searches a multi-dimensional array using a fragment of a sub-array defined in the said composite * * where :: [[a], [b]] -> [a] -> [[a]] * - * @param array $list - * @param array $search + * @param array|object $list + * @param array|object $search * @return array * @example * @@ -30,22 +29,42 @@ * ], ['name' => 'jordan']) * => [['pos' => 'sg', 'name' => 'jordan']] */ -function where(array $list, array $search): array +function where($list, $search): array { - list($searchKey, $searchVal) = head(toPairs($search)); - - $whereFn = function (array $acc = []) use ($searchKey, $searchVal, $list) { - foreach ($list as $index => $value) { - if ( - isset($list[$index][$searchKey]) && - $list[$index][$searchKey] == $searchVal - ) { - $acc[] = $value; + return fold( + function ( + array $acc, + $value, + $key + ) use ($search): array { + if (!(\is_object($search) || \is_array($search))) { + return $acc; } - } - return $acc; - }; + foreach ($search as $idx => $nxt) { + if (\is_array($value) || \is_object($value)) { + foreach ($value as $ikey => $ival) { + if (equals($ikey, $idx) && equals($ival, $nxt, true)) { + $acc[] = $value; + } + + if (\is_array($ival) || \is_object($ival)) { + $acc = extend( + $acc, + where($value, $search) + ); + } + } + } else { + if (equals($key, $idx) && equals($value, $nxt, true)) { + $acc[][$key] = $value; + } + } + } - return isArrayOf($list) == 'array' ? $whereFn() : $list; + return $acc; + }, + $list, + [] + ); } diff --git a/src/Functional/index.php b/src/Functional/index.php index 7018798..8fd39ad 100644 --- a/src/Functional/index.php +++ b/src/Functional/index.php @@ -48,6 +48,7 @@ require_once __DIR__ . '/Keys.php'; require_once __DIR__ . '/Last.php'; require_once __DIR__ . '/LastIndexOf.php'; +require_once __DIR__ . '/ListFromPaths.php'; require_once __DIR__ . '/Map.php'; require_once __DIR__ . '/MapDeep.php'; require_once __DIR__ . '/Max.php'; diff --git a/src/Functors/Monads/Bind.php b/src/Functors/Monads/Bind.php index dea1784..7109432 100644 --- a/src/Functors/Monads/Bind.php +++ b/src/Functors/Monads/Bind.php @@ -22,10 +22,10 @@ * bind :: Monad m => m a -> (a -> m b) -> m b * * @param callable $function - * @param object Monad $value + * @param object Monad|null $value * @return object Monad */ -function bind(callable $function, Monad $value = null): Monad +function bind(callable $function, ?Monad $value = null): Monad { return curry( function ($function, $value) { diff --git a/src/Functors/Monads/IO/Internal/_Readline.php b/src/Functors/Monads/IO/Internal/_Readline.php index dd71d12..d29503c 100644 --- a/src/Functors/Monads/IO/Internal/_Readline.php +++ b/src/Functors/Monads/IO/Internal/_Readline.php @@ -23,10 +23,10 @@ * * @internal * @param string $str - * @param callable $handler + * @param callable|null $handler * @return IO */ -function _readline($str = null, callable $handler = null): Monad +function _readline($str = null, ?callable $handler = null): Monad { return IO::of( !\is_null($handler) ? diff --git a/src/Immutable/Collection.php b/src/Immutable/Collection.php index 9022f32..d1a8c31 100644 --- a/src/Immutable/Collection.php +++ b/src/Immutable/Collection.php @@ -28,20 +28,30 @@ class Collection implements */ public function map(callable $func): ImmutableList { - $count = $this->count(); - $list = \extension_loaded('ds') ? - new Vector() : - new \SplFixedArray($count); - - if ($this->list instanceof \SplFixedArray) { - for ($idx = 0; $idx < $count; $idx++) { - $list[$idx] = $func($this->getList()[$idx]); + $list = $this->getList(); + $acc = []; + + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + $acc[] = $func($current); + + $iterator->next(); } } else { - $list = $list->merge($this->getList()->map($func)); + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; + $acc[] = $func($current); + + $idx++; + } } - return new static($list); + return self::from($acc); } /** @@ -70,8 +80,14 @@ public function fold(callable $func, $acc) $list = $this->getList(); if ($list instanceof \SplFixedArray) { - for ($idx = 0; $idx < $list->count(); $idx++) { - $acc = $func($acc, $list[$idx]); + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + $acc = $func($acc, $current); + + $iterator->next(); } return $acc; @@ -85,23 +101,31 @@ public function fold(callable $func, $acc) */ public function slice(int $count): ImmutableList { - $new = null; + $sliced = []; + $list = $this->getList(); - if (\extension_loaded('ds')) { - $new = $this - ->getList() - ->slice($count); - } else { - $list = $this->getList(); - $listCount = $list->count(); - $new = new \SplFixedArray($listCount - $count); + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $idx = 0; + $iterator->rewind(); + + while ($iterator->valid()) { + $includable = $iterator->key() + $count; - for ($idx = 0; $idx < $new->count(); $idx++) { - $new[$idx] = $list[$idx + $count]; + if (isset($list[$includable])) { + $sliced[] = $list[$includable]; + } else { + break; + } + + $idx++; + $iterator->next(); } - } - return new static($new); + return self::from($sliced); + } else { + return new static($list->slice($count)); + } } /** @@ -109,23 +133,10 @@ public function slice(int $count): ImmutableList */ public function merge(ImmutableList $list): ImmutableList { - $oldSize = $this->getSize(); - $combinedSize = $oldSize + $list->getSize(); - $old = $this->list; - - if ($old instanceof \SplFixedArray) { - $old->setSize($combinedSize); - - for ($idx = 0; $idx < $old->count(); $idx++) { - if ($idx > ($oldSize - 1)) { - $old[$idx] = $list->getList()[($idx - $oldSize)]; - } - } - - return $this->update($old); - } + $acc = []; + $acc = f\extend($this->toArray(), $list->toArray()); - return $this->update($old->merge($list)); + return $this->update(self::from($acc)); } /** @@ -135,13 +146,22 @@ public function mergeN(ImmutableList ...$lists): ImmutableList { return $this->merge( \array_shift($lists) - ->triggerMutation(function ($list) use ($lists) { - for ($idx = 0; $idx < \count($lists); $idx++) { - $list->merge($lists[$idx]); - } + ->triggerMutation( + function ($list) use ($lists) { + $idx = 0; + + while ($next = $lists[$idx] ?? null) { + if (!$next instanceof ImmutableList) { + break; + } - return $list; - }) + $list->merge($next); + $idx++; + } + + return $list; + } + ) ); } @@ -152,7 +172,7 @@ public function reverse(): ImmutableList { $list = $this->getList(); - if (\extension_loaded('ds')) { + if ($list instanceof Vector) { $list->reverse(); return new static($list); @@ -175,10 +195,28 @@ public function fill($value, int $start, int $end): ImmutableList { $list = $this->getList(); - for ($idx = 0; $idx < $list->count(); $idx++) { - $list[$idx] = $idx >= $start && $idx <= $end ? - $value : - $list[$idx]; + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $key = $iterator->key(); + $list[$key] = $key >= $start && $key <= $end ? + $value : + $iterator->current(); + + $iterator->next(); + } + } else { + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; + $list[$idx] = $idx >= $start && $idx <= $end ? + $value : + $current; + + $idx++; + } } return new static($list); @@ -192,11 +230,35 @@ public function fetch($key): ImmutableList $list = $this->getList(); $extr = []; - for ($idx = 0; $idx < $list->count(); $idx++) { - $item = $list[$idx]; + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + + if ( + (\is_array($current) || \is_object($current)) && + f\keysExist($current, $key) + ) { + $extr[] = f\pluck($current, $key); + } + + $iterator->next(); + } + } else { + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; + + if ( + (\is_array($current) || \is_object($current)) && + f\keysExist($current, $key) + ) { + $extr[] = f\pluck($current, $key); + } - if (\is_array($item) && \key_exists($key, $item)) { - $extr[] = $item[$key]; + $idx++; } } @@ -208,13 +270,31 @@ public function fetch($key): ImmutableList */ public function unique(): ImmutableList { - $list = $this->getList(); - $acc = []; + $list = $this->getList(); + $acc = []; + + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + if (!\in_array($current, $acc)) { + $acc[] = $current; + } - for ($idx = 0; $idx < $list->count(); $idx++) { - $item = $list[$idx]; - if (!\in_array($item, $acc)) { - $acc[] = $item; + $iterator->next(); + } + } else { + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; + + if (!\in_array($current, $acc)) { + $acc[] = $current; + } + + $idx++; } } @@ -226,29 +306,32 @@ public function unique(): ImmutableList */ public function intersects(ImmutableList $list): bool { + $current = $this->getList(); $intersect = false; - $main = $this->getSize(); - $oth = $list->getSize(); - if ($main > $oth) { - for ($idx = 0; $idx < $oth; $idx++) { - if (\in_array($list->getList()[$idx], $this->toArray())) { - $intersect = true; - } + if ($current instanceof \SplFixedArray) { + $iterator = $current->getIterator(); + $iterator->rewind(); - if (f\equals($intersect, true)) { + while ($iterator->valid()) { + if (\in_array($iterator->current(), $list->toArray())) { + $intersect = true; break; } + + $iterator->next(); } - } elseif ($oth > $main) { - for ($idx = 0; $idx < $main; $idx++) { - if (\in_array($this->getList()[$idx], $list->toArray())) { - $intersect = true; - } + } else { + $idx = 0; + while (isset($current[$idx])) { + $next = $current[$idx]; - if (f\equals($intersect, true)) { + if (\in_array($next, $list->toArray())) { + $intersect = true; break; } + + $idx++; } } @@ -260,11 +343,18 @@ public function intersects(ImmutableList $list): bool */ public function implode(string $delimiter): string { - return \rtrim($this->fold(function (string $fold, $elem) use ($delimiter) { - $fold .= f\concat($delimiter, $elem, ''); - - return $fold; - }, ''), $delimiter); + return \preg_replace( + \sprintf('/%s$/', \preg_quote($delimiter, '/')), + '', + $this->fold( + function (string $fold, $elem) use ($delimiter) { + $fold .= f\concat($delimiter, $elem, ''); + + return $fold; + }, + '' + ) + ); } /** @@ -280,19 +370,38 @@ public function reject(callable $func): ImmutableList */ public function any(callable $func): bool { - $list = $this->getList(); - $size = $list->count(); - $result = false; + $any = false; + $list = $this->getList(); - for ($idx = 0; $idx < $size; $idx += 1) { - if ($func($list[$idx])) { - $result = true; + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + + if ($func($current)) { + $any = true; + break; + } + + $iterator->next(); + } + } else { + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; + + if ($func($current)) { + $any = true; + break; + } - break; + $idx++; } } - return $result; + return $any; } /** @@ -300,7 +409,38 @@ public function any(callable $func): bool */ public function every(callable $func): bool { - return f\equals($this->reject($func)->getSize(), 0); + $every = true; + $list = $this->getList(); + + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + + if (!$func($current)) { + $every = false; + break; + } + + $iterator->next(); + } + } else { + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; + + if (!$func($current)) { + $every = false; + break; + } + + $idx++; + } + } + + return $every; } /** @@ -393,30 +533,39 @@ private function update($list) * @internal template for filtration operations * @param callable $func * @param bool $pos + * @return ImmutableList */ private function filterOperation(callable $func, bool $pos = true): ImmutableList { + $filter = []; $list = $this->getList(); - $count = $list->count(); - $init = 0; - $new = \extension_loaded('ds') ? - new Vector() : - new \SplFixedArray($list->count()); - for ($idx = 0; $idx < $count; $idx++) { - if ($pos ? $func($list[$idx]) : !$func($list[$idx])) { - if ($list instanceof \SplFixedArray) { - $new[$init++] = $list[$idx]; - } else { - $new[] = $list[$idx]; + if ($list instanceof \SplFixedArray) { + $iterator = $list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + + if ($pos ? $func($current) : !$func($current)) { + $filter[] = $current; } + + $iterator->next(); } - } + } else { + $idx = 0; + while (isset($list[$idx])) { + $current = $list[$idx]; - if ($list instanceof \SplFixedArray) { - $new->setSize($init); + if ($pos ? $func($current) : !$func($current)) { + $filter[] = $current; + } + + $idx++; + } } - return new static($new); + return self::from($filter); } } diff --git a/src/Immutable/CommonTrait.php b/src/Immutable/CommonTrait.php index b639082..64dd4ff 100644 --- a/src/Immutable/CommonTrait.php +++ b/src/Immutable/CommonTrait.php @@ -52,23 +52,51 @@ public static function from(array $list): ImmutableDataStructure */ public function contains($element): bool { - $list = $this->list; - $count = $list->count(); - $acc = []; - - for ($idx = 0; $idx < $count; $idx++) { - $item = $list[$idx]; - $acc[] = \is_object($item) || \is_array($item) ? - f\mapDeep( - function ($val) use ($element): bool { - return f\equals($val, $element, true);// $val == $element; - }, - $item - ) : - f\equals($element, $item, true);// $element == $item; + $exists = false; + + if ($this->list instanceof \SplFixedArray) { + $iterator = $this->list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + $current = $iterator->current(); + + if ($current instanceof ImmutableDataStructure) { + $exists = $next->contains($element); + } elseif (\is_array($current) || \is_object($current)) { + $exists = self::checkContains($current, $element); + } else { + $exists = f\equals($current, $element, true); + } + + if ($exists) { + break; + } + + $iterator->next(); + } + } else { + $idx = 0; + while (isset($this->list[$idx])) { + $current = $this->list[$idx]; + + if ($current instanceof ImmutableDataStructure) { + $exists = $next->contains($element); + } elseif (\is_array($current) || \is_object($current)) { + $exists = self::checkContains($current, $element); + } else { + $exists = f\equals($current, $element, true); + } + + if ($exists) { + break; + } + + $idx++; + } } - return self::checkContains($acc); + return $exists; } /** @@ -76,7 +104,14 @@ function ($val) use ($element): bool { */ public function head() { - return $this->list[0]; + if ($this->list instanceof \SplFixedArray) { + $iterator = $this->list->getIterator(); + $iterator->rewind(); + + return $iterator->current(); + } + + return $this->list->first(); } /** @@ -86,9 +121,26 @@ public function tail(): ImmutableDataStructure { $list = $this->list; $acc = []; - - for ($idx = 1; $idx < $list->count(); $idx++) { - $acc[] = $list[$idx]; + $idx = 1; + + if ($this->list instanceof \SplFixedArray) { + $iterator = $this->list->getIterator(); + $iterator->rewind(); + + while ($iterator->valid()) { + if ($iterator->key() >= $idx) { + $acc[] = $iterator->current(); + } + + $iterator->next(); + } + } else { + while (isset($this->list[$idx])) { + $value = $this->list[$idx]; + $acc[] = $value; + + $idx++; + } } return self::from($acc); @@ -99,7 +151,9 @@ public function tail(): ImmutableDataStructure */ public function last() { - return $this->list[$this->count() - 1]; + return $this->list instanceof \SplFixedArray ? + $this->list[$this->count() - 1] : + $this->list->last(); } /** @@ -130,15 +184,24 @@ public function count(): int * * checkContains :: [a] -> Bool * - * @param array $list + * @param array|object $list * @return bool */ - private static function checkContains(array $list): bool + private static function checkContains($haystack, $needle): bool { - $comp = f\compose(f\flatten, function (array $val) { - return \in_array(true, $val); - }); + $exists = false; + + foreach ($haystack as $value) { + if (\is_object($value) || \is_array($value)) { + $exists = self::checkContains($value, $needle); + } + + if (f\equals($needle, $value, true)) { + $exists = true; + break; + } + } - return $comp($list); + return $exists; } } diff --git a/src/Immutable/Tuple.php b/src/Immutable/Tuple.php index dac0843..8515d11 100644 --- a/src/Immutable/Tuple.php +++ b/src/Immutable/Tuple.php @@ -28,7 +28,18 @@ class Tuple implements \Countable, ImmutableDataStructure */ public function fst() { - return $this->fetchFromPair(0); + if (!equals($this->count(), 2)) { + throw new TupleException(TupleException::PAIR_ERRMSG); + } + + if ($this->list instanceof \SplFixedArray) { + $iterator = $this->list->getIterator(); + $iterator->rewind(); + + return $iterator->current(); + } + + return $this->list->first(); } /** @@ -41,7 +52,27 @@ public function fst() */ public function snd() { - return $this->fetchFromPair(1); + if (!equals($this->count(), 2)) { + throw new TupleException(TupleException::PAIR_ERRMSG); + } + + if ($this->list instanceof \SplFixedArray) { + $iterator = $this->list->getIterator(); + $iterator->rewind(); + $idx = 0; + while ($iterator->valid()) { + if (equals($idx, 1)) { + break; + } + + $idx++; + $iterator->next(); + } + + return $iterator->current(); + } + + return $this->list->last(); } /** @@ -74,20 +105,4 @@ public function get(int $index) { return $this->offsetGet($index); } - - /** - * fetchFromPair method - * - * @internal - * @param int $index - * @return mixed - */ - private function fetchFromPair(int $index) - { - if (!equals($this->count(), 2)) { - throw new TupleException(TupleException::PAIR_ERRMSG); - } - - return $this->get($index); - } } diff --git a/tests/Functional/AssocPathTest.php b/tests/Functional/AssocPathTest.php index 01bc3ab..48c048a 100644 --- a/tests/Functional/AssocPathTest.php +++ b/tests/Functional/AssocPathTest.php @@ -19,7 +19,13 @@ public static function contextProvider() (object) ['foo' => (object) ['bar' => ['baz' => 3]]], ['foo', 'bar', 'baz'], 'baz', - (object) ['foo' => (object) ['bar' => ['baz' => 'baz']]], + (object) [ + 'foo' => (object) [ + 'bar' => (object) [ + 'baz' => 'baz', + ], + ], + ], ], [ [], @@ -27,6 +33,22 @@ public static function contextProvider() 'bar', ['foo' => ['bar' => [1 => 'bar']]], ], + [ + (object) [ + 'foo' => 12, + 'bar' => (object) \range(1, 3), + ], + 'foo.bar.qux', + 'quux', + (object) [ + 'foo' => (object) [ + 'bar' => (object) [ + 'qux' => 'quux', + ], + ], + 'bar' => (object) \range(1, 3), + ], + ], ]; } diff --git a/tests/Functional/HeadTest.php b/tests/Functional/HeadTest.php index 0882733..644da07 100644 --- a/tests/Functional/HeadTest.php +++ b/tests/Functional/HeadTest.php @@ -12,6 +12,23 @@ public static function contextProvider() [[\range(1, 20)], 1], [[(object) ['foo', 'bar']], 'foo'], [[[], 0], 0], + [ + [ + new class () { + public $x = 12; + public $y = 'quux'; + public $z = false; + }, + ], + 12, + ], + [ + [ + (object) [], + 0, + ], + 0, + ], ]; } diff --git a/tests/Functional/IsArrayOfTest.php b/tests/Functional/IsArrayOfTest.php index f9bb55a..847f1c2 100644 --- a/tests/Functional/IsArrayOfTest.php +++ b/tests/Functional/IsArrayOfTest.php @@ -14,6 +14,9 @@ public static function contextProvider() [[[2], ['foo']], 'array'], [[1.2, 3.2], 'double'], [[3, 'foo', new \stdClass(2)], 'mixed'], + [(object) [], null], + [[], null], + [(object) ['foo' => 'qux', 'bar' => 'quux'], 'string'], ]; } diff --git a/tests/Functional/LastTest.php b/tests/Functional/LastTest.php index d015dbb..09fb059 100644 --- a/tests/Functional/LastTest.php +++ b/tests/Functional/LastTest.php @@ -12,6 +12,23 @@ public static function contextProvider() [[\range(1, 20)], 20], [[((object) ['foo', 'bar'])], 'bar'], [[[], 'undefined'], 'undefined'], + [ + [ + new class () { + public $x = true; + public $y = 'foo'; + public $z = 1.2221; + }, + ], + 1.2221, + ], + [ + [ + (object) [], + 0, + ], + 0, + ], ]; } diff --git a/tests/Functional/ListFromPathsTest.php b/tests/Functional/ListFromPathsTest.php new file mode 100644 index 0000000..3f366b2 --- /dev/null +++ b/tests/Functional/ListFromPathsTest.php @@ -0,0 +1,112 @@ + 12, + 'foo.qux' => 'foo' + ] + ], + [ + 'foo' => [ + 'bar' => 12, + 'qux' => 'foo' + ] + ] + ], + [ + [ + [ + 'foo:bar' => 12, + 'foo:qux' => 'foo' + ] + ], + [ + 'foo:bar' => 12, + 'foo:qux' => 'foo' + ] + ], + [ + [ + (object) [ + 'foo.bar' => 12, + 'foo.qux.0' => 'foo', + 'foo.qux.2' => 'baz', + 'quux' => \range(1, 3) + ] + ], + [ + 'foo' => [ + 'bar' => 12, + 'qux' => [ + 0 => 'foo', + 2 => 'baz' + ] + ], + 'quux' => \range(1, 3) + ] + ], + [ + [\range(1, 3)], + \range(1, 3) + ], + [ + [ + (object) [ + 'foo.bar' => 12, + 'foo.qux.0' => 'foo', + 'foo.qux.2' => 'baz', + 'quux' => \range(1, 3) + ], + ':' + ], + [ + 'foo.bar' => 12, + 'foo.qux.0' => 'foo', + 'foo.qux.2' => 'baz', + 'quux' => \range(1, 3) + ] + ], + [ + [ + [ + 'foo->bar' => 12, + 'foo->qux->0' => 'foo', + 'foo->qux->2' => 'baz', + 'quux' => \range(1, 3), + ], + '->' + ], + [ + 'foo' => [ + 'bar' => 12, + 'qux' => [ + 0 => 'foo', + 2 => 'baz' + ], + ], + 'quux' => \range(1, 3), + ] + ] + ]; + } + + /** + * @dataProvider contextProvider + */ + public function testlistFromPathsCreatesListFromAnotherListThatContainsPathsAndTheValuesAssociatedWithThem(array $args, $result) + { + $list = f\listFromPaths(...$args); + + $this->assertEquals($result, $list); + } +} diff --git a/tests/Functional/TailTest.php b/tests/Functional/TailTest.php index cfad9a4..fe8937d 100644 --- a/tests/Functional/TailTest.php +++ b/tests/Functional/TailTest.php @@ -9,8 +9,14 @@ class TailTest extends \PHPUnit\Framework\TestCase public static function contextProvider() { return [ - [(object) \range(1, 3), (object) [1 => 2, 2 => 3]], - [['foo', 'bar'], [1 => 'bar']], + [ + (object) \range(1, 3), + (object) [1 => 2, 2 => 3], + ], + [ + ['foo', 'bar'], + [1 => 'bar'], + ], ]; } diff --git a/tests/Functional/WhereTest.php b/tests/Functional/WhereTest.php index 65baa2c..96e1502 100644 --- a/tests/Functional/WhereTest.php +++ b/tests/Functional/WhereTest.php @@ -11,12 +11,49 @@ public static function contextProvider() return [ [ [ - ['name' => 'dwayne', 'pos' => 'sg'], - ['name' => 'james', 'pos' => 'sf'], - ['name' => 'demarcus', 'pos' => 'c'], + [ + 'name' => 'dwayne', + 'pos' => 'sg' + ], + [ + 'name' => 'james', + 'pos' => 'sf', + ], + [ + 'name' => 'demarcus', + 'pos' => 'c', + ], ], ['pos' => 'c'], - [['name' => 'demarcus', 'pos' => 'c']], + [ + [ + 'name' => 'demarcus', + 'pos' => 'c', + ], + ], + ], + [ + (object) [ + [ + 'name' => 'michael', + 'pos' => 'sg' + ], + [ + 'name' => 'olajuwon', + 'pos' => 'c', + 'co-star' => [ + 'name' => 'drexler', + 'pos' => 'sg', + ], + ], + ], + ['name' => 'drexler'], + [ + [ + 'name' => 'drexler', + 'pos' => 'sg', + ], + ], ], ]; } diff --git a/tests/Immutable/TupleTest.php b/tests/Immutable/TupleTest.php index 798dd61..aa8e9c4 100644 --- a/tests/Immutable/TupleTest.php +++ b/tests/Immutable/TupleTest.php @@ -115,7 +115,7 @@ public function fstExtractsFirstComponentOfTuplePair() // extract error from failed pair creation $this->assertEquals(imm\TupleException::PAIR_ERRMSG, $fst($list)); - $this->assertEquals('foo', $fst(f\dropLeft($list, 1))); + $this->assertEquals('foo', $fst(\array_slice($list, 1))); }); } @@ -138,7 +138,7 @@ public function sndExtractsSecondComponentOfTuplePair() }); $this->assertEquals(imm\TupleException::PAIR_ERRMSG, $snd($list)); - $this->assertEquals(9, $snd(f\dropLeft($list, 1))); + $this->assertEquals(9, $snd(\array_slice($list, 1))); }); } @@ -159,7 +159,7 @@ public function swapInvertsTuplePairOrder() $swap = f\toException(function ($list) { return Tuple::from($list)->swap(); }); - $data = $swap(f\dropLeft($list, 1)); + $data = $swap(\array_slice($list, 1)); $this->assertEquals(imm\TupleException::PAIR_ERRMSG, $swap($list)); $this->assertEquals('bar', $data->get(0));