initial commit
This commit is contained in:
parent
f6e4a96287
commit
ff8bf631d2
8 changed files with 571 additions and 13 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/vendor
|
||||
/composer.lock
|
24
LICENSE
24
LICENSE
|
@ -1,21 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Joby's PHP Toolbox: https://code.byjoby.com/php-toolbox/
|
||||
Copyright (c) 2024 Joby Elliott
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
# php-toolbox
|
||||
# Joby's PHP Toolbox
|
||||
|
||||
A lightweight collection of useful general purpose PHP tools with no dependencies. Committed to always at least having minimal dependencies.
|
||||
|
||||
## Development status
|
||||
|
||||
Anything that's in the `main` branch should have tests and be a stable API. I haven't set a version number yet because I don't want to call it 1.0 until I actually have a more significant number of features in here.
|
||||
|
|
22
composer.json
Normal file
22
composer.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "joby/toolbox",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Joby\\Toolbox\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Joby Elliot",
|
||||
"email": "code@byjoby.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "~8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^11.2"
|
||||
}
|
||||
}
|
164
src/Sorting/Sort.php
Normal file
164
src/Sorting/Sort.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Joby's PHP Toolbox: https://code.byjoby.com/php-toolbox/
|
||||
* Copyright (c) 2024 Joby Elliott
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Joby\Toolbox\Sorting;
|
||||
|
||||
/**
|
||||
* Static class for sorting arrays using sets of callbacks. This is useful for
|
||||
* cases where you want to sort an array by multiple criteria, and progressively
|
||||
* move down the list of sorting criteria as needed to break ties.
|
||||
*
|
||||
* This class is a wrapper around the Sorter class, and is useful for cases
|
||||
* where you only need to sort a single array and don't need to reuse the same
|
||||
* set of sorters on multiple arrays. If you're going to be sorting many arrays
|
||||
* using the same set of sorters it would be more efficient to instantiate a
|
||||
* Sorter object and reuse it.
|
||||
*/
|
||||
class Sort
|
||||
{
|
||||
/**
|
||||
* Sort an array using an array of callable sorters that will be passed
|
||||
* pairs of items from the array, and the array will be sorted based on the
|
||||
* results of the first sorter that returns a non-zero value for any
|
||||
* given pair of items.
|
||||
*
|
||||
* This method takes its array by reference and sorts it in place to reduce
|
||||
* memory use.
|
||||
*
|
||||
* Example sorting integers:
|
||||
*
|
||||
* ```php
|
||||
* $data = [3, 1, 4, 1, 5, 9];
|
||||
* Sort::sort($data, fn ($a, $b) => $a <=> $b);
|
||||
* ```
|
||||
*
|
||||
* Example sorting integers with even numbers first:
|
||||
*
|
||||
* ```php
|
||||
* $data = [3, 1, 4, 1, 5, 9];
|
||||
* Sort::sort($data, fn ($a, $b) => $a % 2 <=> $b % 2, fn ($a, $b) => $a <=> $b);
|
||||
* ```
|
||||
*/
|
||||
public static function sort(array &$data, callable ...$comparisons): void
|
||||
{
|
||||
(new Sorter(...$comparisons))->sort($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the order of a sorting callback.
|
||||
*
|
||||
* Example sorting integers in reverse order:
|
||||
*
|
||||
* ```php
|
||||
* $data = [3, 1, 4, 1, 5, 9];
|
||||
* Sort::sort($data, Sort::reverse(fn ($a, $b) => $a <=> $b));
|
||||
* ```
|
||||
*/
|
||||
public static function reverse(callable $comparison): callable
|
||||
{
|
||||
return function ($a, $b) use ($comparison) {
|
||||
return $comparison($b, $a);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comparison callback that will call the same method on two
|
||||
* objects and compare the results for sorting, optionally passing arguments
|
||||
* to the methods.
|
||||
*
|
||||
* Example given a class with a method `getNumber()`:
|
||||
*
|
||||
* ```php
|
||||
* $data = [...]; // array of objects with getNumber() method
|
||||
* Sort::sort($data, Sort::compareMethods('getNumber'));
|
||||
* ```
|
||||
*/
|
||||
public static function compareMethods(string $method_name, mixed ...$args): callable
|
||||
{
|
||||
return function (object $a, object $b) use ($method_name, $args): int {
|
||||
return call_user_func_array([$a, $method_name], $args) <=> call_user_func_array([$b, $method_name], $args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comparison callback that will compare the values of the same
|
||||
* property on two objects for sorting.
|
||||
*
|
||||
* Example given a class with a property `itemName`:
|
||||
*
|
||||
* ```php
|
||||
* $data = [...]; // array of objects with itemName property
|
||||
* Sort::sort($data, Sort::compareProperties('itemName'));
|
||||
* ```
|
||||
*/
|
||||
public static function compareProperties(string $property_name): callable
|
||||
{
|
||||
return function (object $a, object $b) use ($property_name): int {
|
||||
return $a->$property_name <=> $b->$property_name;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comparison callback that will compare the values of the same
|
||||
* key in two arrays for sorting.
|
||||
*
|
||||
* Example sorting by the 'name' key:
|
||||
*
|
||||
* ```php
|
||||
* $data = [
|
||||
* ['name' => 'apple'],
|
||||
* ['name' => 'banana'],
|
||||
* ['name' => 'cherry'],
|
||||
* ];
|
||||
* Sort::sort($data, Sort::compareArrayValues('name'));
|
||||
* ```
|
||||
*/
|
||||
public static function compareArrayValues(string $key): callable
|
||||
{
|
||||
return function (array $a, array $b) use ($key): int {
|
||||
return @$a[$key] <=> @$b[$key];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comparison callback that will run a callback on items and
|
||||
* compare the results for sorting.
|
||||
*
|
||||
* Example sorting by the length of strings:
|
||||
*
|
||||
* ```php
|
||||
* $data = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
|
||||
* Sort::sort($data, Sort::compareCallbackResults(strlen(...)));
|
||||
* ```
|
||||
*/
|
||||
public static function compareCallbackResults(callable $callback): callable
|
||||
{
|
||||
return function (mixed $a, mixed $b) use ($callback): int {
|
||||
return $callback($a) <=> $callback($b);
|
||||
};
|
||||
}
|
||||
}
|
89
src/Sorting/Sorter.php
Normal file
89
src/Sorting/Sorter.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Joby's PHP Toolbox: https://code.byjoby.com/php-toolbox/
|
||||
* Copyright (c) 2024 Joby Elliott
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Joby\Toolbox\Sorting;
|
||||
|
||||
/**
|
||||
* Class for sorting arrays using sets of callbacks. This is useful for cases
|
||||
* where you want to sort an array by multiple criteria, and progressively move
|
||||
* down the list of sorting criteria as needed to break ties. Instantiating a
|
||||
* Sorter object can be useful if you want to apply the same set of sorters to
|
||||
* multiple input arrays.
|
||||
*
|
||||
* If you're only sorting one array, it may be more convenient to use the static
|
||||
* form by calling Sort::sort() instead.
|
||||
*/
|
||||
class Sorter
|
||||
{
|
||||
protected array $comparisons = [];
|
||||
|
||||
/**
|
||||
* Create a new Sorter object with the given list of sorting callbacks.
|
||||
* The sorters will be called in order, and the array will be sorted based
|
||||
* on the first one to return a non-zero value.
|
||||
*/
|
||||
public function __construct(callable ...$comparisons)
|
||||
{
|
||||
$this->comparisons = $comparisons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more sorting callbacks to this sorter. The new callbacks will
|
||||
* be appended to the end of the existing list of sorters.
|
||||
*/
|
||||
public function addComparison(callable ...$comparisons): static
|
||||
{
|
||||
foreach ($comparisons as $sorter) {
|
||||
$this->comparisons[] = $sorter;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort an array using the current list of callbacks. This method takes its
|
||||
* array by reference and sorts it in place to reduce memory use.
|
||||
*/
|
||||
public function sort(array &$data): static
|
||||
{
|
||||
usort($data, static::sortItems(...));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which of two items should go first, or if they are a tie.
|
||||
*/
|
||||
protected function sortItems($a, $b): int
|
||||
{
|
||||
foreach ($this->comparisons as $comparison) {
|
||||
$result = intval($comparison($a, $b));
|
||||
if ($result !== 0) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
166
tests/Sorting/SortTest.php
Normal file
166
tests/Sorting/SortTest.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Joby's PHP Toolbox: https://code.byjoby.com/php-toolbox/
|
||||
* Copyright (c) 2024 Joby Elliott
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Joby\Toolbox\Sorting;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test cases for the static Sort class, including its helpers for creating
|
||||
* useful comparison callbacks.
|
||||
*/
|
||||
class SortTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* This is just ensuring that the static class is set up reasonably okay,
|
||||
* because Sorter is tested elsewhere and this just uses that.
|
||||
*/
|
||||
function testSort()
|
||||
{
|
||||
$data = [3, 1, 4, 1, 5, 9];
|
||||
Sort::sort($data, fn ($a, $b) => $a <=> $b);
|
||||
$this->assertEquals([1, 1, 3, 4, 5, 9], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the reverse method to ensure it flips the order of a comparison.
|
||||
*/
|
||||
function testReverse()
|
||||
{
|
||||
$data = [3, 1, 4, 1, 5, 9];
|
||||
Sort::sort($data, Sort::reverse(fn ($a, $b) => $a <=> $b));
|
||||
$this->assertEquals([9, 5, 4, 3, 1, 1], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the compareProperties method to ensure it creates a comparison callback
|
||||
* that calls the same method on two objects and compares the results.
|
||||
*/
|
||||
function testCompareProperties()
|
||||
{
|
||||
$data = [
|
||||
(object) ['name' => 'apple'],
|
||||
(object) ['name' => 'banana'],
|
||||
(object) ['name' => 'cherry'],
|
||||
(object) ['name' => 'date'],
|
||||
(object) ['name' => 'elderberry'],
|
||||
];
|
||||
Sort::sort($data, Sort::compareProperties('name'));
|
||||
$this->assertEquals([
|
||||
(object) ['name' => 'apple'],
|
||||
(object) ['name' => 'banana'],
|
||||
(object) ['name' => 'cherry'],
|
||||
(object) ['name' => 'date'],
|
||||
(object) ['name' => 'elderberry'],
|
||||
], $data);
|
||||
}
|
||||
|
||||
function testCompareMethods()
|
||||
{
|
||||
$data = [
|
||||
new SortTestComparePropertiesHarness(9),
|
||||
new SortTestComparePropertiesHarness(117),
|
||||
new SortTestComparePropertiesHarness(28),
|
||||
new SortTestComparePropertiesHarness(6),
|
||||
new SortTestComparePropertiesHarness(212),
|
||||
new SortTestComparePropertiesHarness(323),
|
||||
];
|
||||
// default value of method is mod 10, so it should sort by the last digit
|
||||
Sort::sort($data, Sort::compareMethods('value'));
|
||||
$this->assertEquals([
|
||||
new SortTestComparePropertiesHarness(212),
|
||||
new SortTestComparePropertiesHarness(323),
|
||||
new SortTestComparePropertiesHarness(6),
|
||||
new SortTestComparePropertiesHarness(117),
|
||||
new SortTestComparePropertiesHarness(28),
|
||||
new SortTestComparePropertiesHarness(9),
|
||||
], $data);
|
||||
// now sort passing an argument, mod 100 so it should sort by the last 2 digits
|
||||
Sort::sort($data, Sort::compareMethods('value', 100));
|
||||
$this->assertEquals([
|
||||
new SortTestComparePropertiesHarness(6),
|
||||
new SortTestComparePropertiesHarness(9),
|
||||
new SortTestComparePropertiesHarness(212),
|
||||
new SortTestComparePropertiesHarness(117),
|
||||
new SortTestComparePropertiesHarness(323),
|
||||
new SortTestComparePropertiesHarness(28),
|
||||
|
||||
], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the callbacks created by compareArrayValues() succesfully sort
|
||||
* arrays by a particular value within them.
|
||||
*/
|
||||
function testCompareArrayValues()
|
||||
{
|
||||
$data = [
|
||||
['name' => 'apple'],
|
||||
['foo' => 'bar', 'name' => 'banana'],
|
||||
['name' => 'cherry'],
|
||||
['name' => 'date', 'buzz' => 'baz'],
|
||||
['name' => 'elderberry'],
|
||||
];
|
||||
Sort::sort($data, Sort::compareArrayValues('name'));
|
||||
$this->assertEquals([
|
||||
['name' => 'apple'],
|
||||
['foo' => 'bar', 'name' => 'banana'],
|
||||
['name' => 'cherry'],
|
||||
['name' => 'date', 'buzz' => 'baz'],
|
||||
['name' => 'elderberry'],
|
||||
], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the compareCallbackResults() method works as expected by
|
||||
* sorting a list of integers by their value mod 10.
|
||||
*/
|
||||
function testCompareCallbackResults()
|
||||
{
|
||||
$data = [9, 17, 28, 6, 12, 23];
|
||||
Sort::sort($data, Sort::compareCallbackResults(fn ($a) => $a % 10));
|
||||
$this->assertEquals([12, 23, 6, 17, 28, 9], $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Harness object for testing the compareProperties method.
|
||||
*/
|
||||
class SortTestComparePropertiesHarness
|
||||
{
|
||||
public int $value;
|
||||
|
||||
public function __construct(int $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function value(int $mod = 10): int
|
||||
{
|
||||
return $this->value % $mod;
|
||||
}
|
||||
}
|
110
tests/Sorting/SorterTest.php
Normal file
110
tests/Sorting/SorterTest.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Joby's PHP Toolbox: https://code.byjoby.com/php-toolbox/
|
||||
* Copyright (c) 2024 Joby Elliott
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace Joby\Toolbox\Sorting;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test cases for the Sorter class.
|
||||
*/
|
||||
class SorterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Confirm that sorting an empty array works.
|
||||
*/
|
||||
public function testEmptyArray()
|
||||
{
|
||||
$data = [];
|
||||
$sorter = new Sorter();
|
||||
$sorter->sort($data);
|
||||
$this->assertEquals([], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that sorting an array of integers works.
|
||||
*/
|
||||
public function testSortingIntegers()
|
||||
{
|
||||
$data = [3, 1, 4, 1, 5, 9];
|
||||
$sorter = new Sorter(fn ($a, $b) => $a <=> $b);
|
||||
$sorter->sort($data);
|
||||
$this->assertEquals([1, 1, 3, 4, 5, 9], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that sorting an array of strings works.
|
||||
*/
|
||||
public function testSortingStrings()
|
||||
{
|
||||
$data = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
|
||||
$sorter = new Sorter(fn ($a, $b) => strcmp($a, $b));
|
||||
$sorter->sort($data);
|
||||
$this->assertEquals(['apple', 'banana', 'cherry', 'date', 'elderberry'], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that sorting an array of strings by length works.
|
||||
*/
|
||||
public function testSortingByLength()
|
||||
{
|
||||
$data = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
|
||||
$sorter = new Sorter(fn ($a, $b) => strlen($a) <=> strlen($b));
|
||||
$sorter->sort($data);
|
||||
$this->assertEquals(['date', 'apple', 'banana', 'cherry', 'elderberry'], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that sorting an array of strings by length works with a second comparison
|
||||
* using strcmp to sort alphabetically in the case of a tie.
|
||||
*/
|
||||
public function testSortingByLengthWithTieBreaker()
|
||||
{
|
||||
$data = ['cc', 'c', 'ccc', 'aaa', 'aa', 'a', 'b', 'bb', 'bbb'];
|
||||
$sorter = new Sorter(
|
||||
fn ($a, $b) => strlen($a) <=> strlen($b),
|
||||
fn ($a, $b) => strcmp($a, $b)
|
||||
);
|
||||
$sorter->sort($data);
|
||||
$this->assertEquals(['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc'], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that adding sorters using addSorter() works as expected.
|
||||
*/
|
||||
public function testAddingSorters()
|
||||
{
|
||||
$data = ['cc', 'c', 'ccc', 'aaa', 'aa', 'a', 'b', 'bb', 'bbb'];
|
||||
$sorter = new Sorter();
|
||||
$sorter->addComparison(
|
||||
fn ($a, $b) => strlen($a) <=> strlen($b),
|
||||
fn ($a, $b) => strcmp($a, $b)
|
||||
);
|
||||
$sorter->sort($data);
|
||||
$this->assertEquals(['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc'], $data);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue