Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ ArrayLookup is a fast lookup library that help you verify and search `array` and
Features
--------

- [x] Verify at least times: `once()`, `twice()`, `times()`
- [x] Verify at most times: `once()`, `twice()`, `times()`
- [x] Verify exact times: `once()`, `twice()`, `times()`
- [x] Search data: `first()`, `last()`, `rows()`, `partition()`
- [x] Collect data with filter and transform
- [x] Verify at least times: [`once()`](#1-atleastonce), [`twice()`](#2-atleasttwice), [`times()`](#3-atleasttimes)
- [x] Verify at most times: [`once()`](#1-atmostonce), [`twice()`](#2-atmosttwice), [`times()`](#3-atmosttimes)
- [x] Verify exact times: [`once()`](#1-onlyonce), [`twice()`](#2-onlytwice), [`times()`](#3-onlytimes)
- [x] Verify in interval range: [`isInclusiveOf()`](#1-intervalisinclusiveof), [`isExclusiveOf()`](#2-intervalisexclusiveof)
- [x] Search data: [`first()`](#1-finderfirst), [`last()`](#2-finderlast), [`rows()`](#3-finderrows), [`partition()`](#4-finderpartition)
- [x] Collect data with [filter and transform](#e-collector)

Installation
------------
Expand Down Expand Up @@ -302,7 +303,52 @@ $times = 2;
var_dump(Only::times($data, $filter, $times)) // false
```

**D. Finder**
**D. Interval**
---------------

#### 1. `Interval::isInclusiveOf()`

It verify that data has filtered found items within min and max (inclusive).

```php
use ArrayLookup\Interval;

$orders = [
['status' => 'paid'],
['status' => 'paid'],
['status' => 'pending'],
['status' => 'paid'],
];

$filter = static fn(array $order): bool => $order['status'] === 'paid';

// inclusive means min and max boundaries are allowed
var_dump(Interval::isInclusiveOf($orders, $filter, 3, 5)) // true
var_dump(Interval::isInclusiveOf($orders, $filter, 2, 5)) // true
```

#### 2. `Interval::isExclusiveOf()`

It verify that data has filtered found items between min and max (exclusive).

```php
use ArrayLookup\Interval;

$orders = [
['status' => 'paid'],
['status' => 'paid'],
['status' => 'pending'],
['status' => 'paid'],
];

$filter = static fn(array $order): bool => $order['status'] === 'paid';

// exclusive means strictly between min and max
var_dump(Interval::isExclusiveOf($orders, $filter, 3, 5)) // false
var_dump(Interval::isExclusiveOf($orders, $filter, 2, 5)) // true
```

**E. Finder**
---------------

#### 1. `Finder::first()`
Expand Down Expand Up @@ -473,7 +519,7 @@ var_dump($even); // [0 => 10, 2 => 30]
var_dump($odd); // [1 => 20, 3 => 40]
```

**E. Collector**
**F. Collector**
---------------

It collect filtered data, with new transformed each data found:
Expand Down
82 changes: 82 additions & 0 deletions src/Interval.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace ArrayLookup;

use ArrayLookup\Assert\Filter;
use Traversable;
use Webmozart\Assert\Assert;

final class Interval
{
/**
* @param array<int|string, mixed>|Traversable<int|string, mixed> $data
* @param callable(mixed $datum, int|string|null $key): bool $filter
*/
public static function isInclusiveOf(
iterable $data,
callable $filter,
int $min,
int $max
): bool {
Assert::greaterThan($min, 0);
Assert::greaterThan($max, 0);
Assert::lessThanEq($min, $max);
Filter::boolean($filter);

$totalFound = 0;
foreach ($data as $key => $datum) {
$isFound = $filter($datum, $key);

if (! $isFound) {
continue;
}

++$totalFound;

if ($totalFound > $max) {
return false;
}
}

return $totalFound >= $min;
}

/**
* @param array<int|string, mixed>|Traversable<int|string, mixed> $data
* @param callable(mixed $datum, int|string|null $key): bool $filter
*/
public static function isExclusiveOf(
iterable $data,
callable $filter,
int $min,
int $max
): bool {
Assert::greaterThan($min, 0);
Assert::greaterThan($max, 0);
Assert::lessThan($min, $max);
Filter::boolean($filter);

if ($max - $min <= 1) {
return false;
}

$totalFound = 0;
foreach ($data as $key => $datum) {
$isFound = $filter($datum, $key);

if (! $isFound) {
continue;
}

++$totalFound;

if ($totalFound >= $max) {
return false;
}
}

return $totalFound > $min && $totalFound < $max;
}
}
145 changes: 145 additions & 0 deletions tests/IntervalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

declare(strict_types=1);

namespace ArrayLookup\Tests;

use ArrayLookup\Interval;
use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class IntervalTest extends TestCase
{
/**
* @param int[]|string[] $data
*/
#[DataProvider('inclusiveDataProvider')]
public function testIsInclusiveOf(
array $data,
callable $filter,
int $min,
int $max,
bool $expected
): void {
$this->assertSame(
$expected,
Interval::isInclusiveOf($data, $filter, $min, $max)
);
}

/**
* @return Iterator<mixed>
*/
public static function inclusiveDataProvider(): Iterator
{
yield 'min boundary' => [
[1, 2, 3],
static fn($datum): bool => $datum > 1,
2,
5,
true,
];
yield 'inside range' => [
[1, 2, 3, 4, 5],
static fn($datum): bool => $datum > 2,
2,
5,
true,
];
yield 'max boundary' => [
[1, 2, 3, 4, 5],
static fn($datum): bool => $datum >= 1,
2,
5,
true,
];
yield 'below min' => [
[1, 2, 3],
static fn($datum): bool => $datum === 3,
2,
5,
false,
];
yield 'above max' => [
[1, 2, 3, 4, 5, 6],
static fn($datum): bool => $datum >= 1,
2,
5,
false,
];
yield 'with key in filter' => [
['a', 'b', 'c', 'd', 'e'],
static fn(string $datum, int $key): bool => $datum !== 'a' && $key > 0,
2,
5,
true,
];
}

/**
* @param int[]|string[] $data
*/
#[DataProvider('exclusiveDataProvider')]
public function testIsExclusiveOf(
array $data,
callable $filter,
int $min,
int $max,
bool $expected
): void {
$this->assertSame(
$expected,
Interval::isExclusiveOf($data, $filter, $min, $max)
);
}

/**
* @return Iterator<mixed>
*/
public static function exclusiveDataProvider(): Iterator
{
yield 'inside range' => [
[1, 2, 3, 4, 5],
static fn($datum): bool => $datum > 2,
2,
5,
true,
];
yield 'above min only' => [
[1, 2, 3],
static fn($datum): bool => $datum > 1,
2,
5,
false,
];
yield 'at min boundary' => [
[1, 2, 3],
static fn($datum): bool => $datum > 2,
2,
5,
false,
];
yield 'at max boundary' => [
[1, 2, 3, 4, 5],
static fn($datum): bool => $datum >= 1,
2,
5,
false,
];
yield 'above max' => [
[1, 2, 3, 4, 5, 6],
static fn($datum): bool => $datum >= 1,
2,
5,
false,
];
yield 'no space between bounds' => [
[1, 2, 3],
static fn($datum): bool => $datum > 1,
2,
3,
false,
];
}
}