move query helpers to internal namespace

This commit is contained in:
Kacper Donat 2026-10-27 22:22:12 +01:00
parent 483375240b
commit b836bc5e8f
3 changed files with 101 additions and 85 deletions

View File

@ -26,6 +26,7 @@
"./src/Predicates/index.php", "./src/Predicates/index.php",
"./src/Collections/Internal/functions.php", "./src/Collections/Internal/functions.php",
"./src/Collections/Internal/containers.php", "./src/Collections/Internal/containers.php",
"./src/Collections/Internal/queries.php",
"./src/Collections/transforms.php", "./src/Collections/transforms.php",
"./src/Collections/collectors.php", "./src/Collections/collectors.php",
"./src/Collections/queries.php" "./src/Collections/queries.php"

View File

@ -0,0 +1,97 @@
<?php
/**
* Copyright 2026 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Collections\Internal;
use Kadet\Functional\Exception\EmptyCollectionException;
/**
* Yields [key, value] pairs of $data from the end, without copying it where the container allows.
*
* @internal
*
* @param iterable<mixed, mixed> $data
*
* @return \Generator<int, array{mixed, mixed}>
*/
function reversedEntries(iterable $data): \Generator
{
if (is_array($data)) {
for (end($data); ($key = key($data)) !== null; prev($data)) {
yield [$key, current($data)];
}
} elseif ($data instanceof \Ds\Seq || $data instanceof \Ds\Set) {
for ($index = \count($data) - 1; $index >= 0; $index--) {
yield [$index, $data->get($index)];
}
} elseif ($data instanceof \Ds\Map) {
foreach ($data->pairs()->reversed() as $pair) {
yield [$pair->key, $pair->value];
}
} else {
$entries = [];
foreach ($data as $key => $value) {
$entries[] = [$key, $value];
}
yield from array_reverse($entries);
}
}
/**
* Shared implementation of {@see first()} and {@see find()}, so that the exception names the called function.
*
* @internal
*
* @return \Closure(iterable<mixed, mixed>): mixed
*/
function firstOf(string $name, ?callable $predicate, bool $required): \Closure
{
$predicate = $predicate === null ? null : callback($predicate);
return static function (iterable $data) use ($name, $predicate, $required) {
foreach ($data as $key => $value) {
if ($predicate === null || $predicate($value, $key)) {
return $value;
}
}
if ($required) {
throw EmptyCollectionException::noMatch($name, $predicate !== null);
}
return null;
};
}
/**
* Shared implementation of {@see min()} (`$direction` -1) and {@see max()} (1).
*
* @internal
*
* @return \Closure(iterable<mixed, mixed>): mixed
*/
function extremum(int $direction, ?callable $selector): \Closure
{
$selector = $selector === null ? static fn ($value) => $value : callback($selector);
return static function (iterable $data) use ($direction, $selector) {
$best = null;
$first = true;
foreach ($data as $key => $value) {
$value = $selector($value, $key);
if ($first || ($value <=> $best) === $direction) {
$best = $value;
$first = false;
}
}
return $best;
};
}

View File

@ -22,41 +22,11 @@ namespace Kadet\Functional\Collections;
use Kadet\Functional\Exception\EmptyCollectionException; use Kadet\Functional\Exception\EmptyCollectionException;
use function Kadet\Functional\Collections\Internal\callback; use function Kadet\Functional\Collections\Internal\callback;
use function Kadet\Functional\Collections\Internal\extremum;
use function Kadet\Functional\Collections\Internal\firstOf;
use function Kadet\Functional\Collections\Internal\reversedEntries;
use function Kadet\Functional\compile; use function Kadet\Functional\compile;
/**
* Yields [key, value] pairs of $data from the end, without copying it where the container allows.
*
* @internal
*
* @param iterable<mixed, mixed> $data
*
* @return \Generator<int, array{mixed, mixed}>
*/
function reversedEntries(iterable $data): \Generator
{
if (is_array($data)) {
for (end($data); ($key = key($data)) !== null; prev($data)) {
yield [$key, current($data)];
}
} elseif ($data instanceof \Ds\Seq || $data instanceof \Ds\Set) {
for ($index = \count($data) - 1; $index >= 0; $index--) {
yield [$index, $data->get($index)];
}
} elseif ($data instanceof \Ds\Map) {
foreach ($data->pairs()->reversed() as $pair) {
yield [$pair->key, $pair->value];
}
} else {
$entries = [];
foreach ($data as $key => $value) {
$entries[] = [$key, $value];
}
yield from array_reverse($entries);
}
}
/** /**
* Returns the first value for which `$predicate($value, $key)` is truthy, or the first value without a predicate. * Returns the first value for which `$predicate($value, $key)` is truthy, or the first value without a predicate.
* *
@ -145,32 +115,6 @@ function find(callable $predicate, bool $required = false): \Closure
return firstOf('find', $predicate, $required); return firstOf('find', $predicate, $required);
} }
/**
* Shared implementation of {@see first()} and {@see find()}, so that the exception names the called function.
*
* @internal
*
* @return \Closure(iterable<mixed, mixed>): mixed
*/
function firstOf(string $name, ?callable $predicate, bool $required): \Closure
{
$predicate = $predicate === null ? null : callback($predicate);
return static function (iterable $data) use ($name, $predicate, $required) {
foreach ($data as $key => $value) {
if ($predicate === null || $predicate($value, $key)) {
return $value;
}
}
if ($required) {
throw EmptyCollectionException::noMatch($name, $predicate !== null);
}
return null;
};
}
/** /**
* Whether the collection holds `$value`, compared strictly (`Ds\Key::equals` when `$value` implements `Ds\Key`). * Whether the collection holds `$value`, compared strictly (`Ds\Key::equals` when `$value` implements `Ds\Key`).
* *
@ -330,32 +274,6 @@ function max(?callable $selector = null): \Closure
return extremum(1, $selector); return extremum(1, $selector);
} }
/**
* Shared implementation of {@see min()} (`$direction` -1) and {@see max()} (1).
*
* @internal
*
* @return \Closure(iterable<mixed, mixed>): mixed
*/
function extremum(int $direction, ?callable $selector): \Closure
{
$selector = $selector === null ? static fn ($value) => $value : callback($selector);
return static function (iterable $data) use ($direction, $selector) {
$best = null;
$first = true;
foreach ($data as $key => $value) {
$value = $selector($value, $key);
if ($first || ($value <=> $best) === $direction) {
$best = $value;
$first = false;
}
}
return $best;
};
}
/** /**
* Folds the collection into a single value with `$fn($carry, $value, $key)`, starting from `$initial`. * Folds the collection into a single value with `$fn($carry, $value, $key)`, starting from `$initial`.
* *