From 7a922696bdee6e770a4e539df6058e0623dcb8bf Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Fri, 3 Feb 2023 02:11:21 +0300 Subject: [PATCH] Added the ability to change the sort order of logical groups --- README.md | 46 +++++- src/Services/Order.php | 68 ++++++++ src/Sorter.php | 60 ++++--- tests/Sorters/FullGroupsOrderTest.php | 195 +++++++++++++++++++++++ tests/Sorters/PartialGroupsOrderTest.php | 192 ++++++++++++++++++++++ 5 files changed, 539 insertions(+), 22 deletions(-) create mode 100644 src/Services/Order.php create mode 100644 tests/Sorters/FullGroupsOrderTest.php create mode 100644 tests/Sorters/PartialGroupsOrderTest.php diff --git a/README.md b/README.md index 34f4bda..b25684c 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,51 @@ $items = Size::query()->get(); return Sorter::sort($items, 'title'); ``` -> You can see more examples in the [test file](tests/Sorters/StaticallyTest.php). +> You can see more examples in the [test file](tests/Sorters/SorterTest.php). + +### Groups Order + +By default, sizes are sorted by the following logical blocks: + +1. Letter clothing size (XXS, XS, M, L, etc.) +2. Numerical size of clothes and shoes (9, 10, 44-46, 48, etc.) +3. Bra size (70B, 75A, 80C, etc...) +4. Overall dimensions of items (40x38x19 sm, etc.) +5. Other values + +But you can change the order by specifying identifiers as the third parameter: + +```php +use DragonCode\SizeSorter\Enum\Group; +use DragonCode\SizeSorter\Sorter; + +return Sorter::sort($items, groupsOrder: [3, 5, 4, 2, 1]); +// or +return Sorter::sort($items, groupsOrder: [Group::GROUP_3, Group::GROUP_5, Group::GROUP_4, Group::GROUP_2, Group::GROUP_1]); +``` + +The final array will be formed in the specified order: + +``` +3 - 5 - 4 - 2 - 1 +``` + +You can also specify some groups. For example: + +```php +use DragonCode\SizeSorter\Enum\Group; +use DragonCode\SizeSorter\Sorter; + +return Sorter::sort($items, groupsOrder: [3, 5]); +// or +return Sorter::sort($items, groupsOrder: [Group::GROUP_3, Group::GROUP_5]); +``` + +In this case, the first two logical groups will be sorted in the specified order, and the subsequent ones will be in ascending order: + +``` +3 - 5 - 1 - 2 - 4 +``` ## License diff --git a/src/Services/Order.php b/src/Services/Order.php new file mode 100644 index 0000000..e085c4b --- /dev/null +++ b/src/Services/Order.php @@ -0,0 +1,68 @@ + + */ + protected static function groups(): array + { + return Group::values(); + } + + protected static function resolveValue(Group|int $group): int + { + return $group->value ?? $group; + } +} diff --git a/src/Sorter.php b/src/Sorter.php index 53c6663..80d643f 100644 --- a/src/Sorter.php +++ b/src/Sorter.php @@ -7,36 +7,49 @@ use DragonCode\SizeSorter\Enum\Group; use DragonCode\SizeSorter\Services\Collection; use DragonCode\SizeSorter\Services\GroupsDetector; +use DragonCode\SizeSorter\Services\Order; use DragonCode\SizeSorter\Services\Resolver; use DragonCode\SizeSorter\Services\Sorter as SorterService; use DragonCode\Support\Helpers\Ables\Stringable; -use Illuminate\Support\Collection as IlluminateCollection; +use Illuminate\Support\Collection as IC; class Sorter { - public static function sort(IlluminateCollection $items, string $column = 'value'): IlluminateCollection + public static function sort(IC $items, string $column = 'value', ?array $groupsOrder = null): IC { - return static::handle($items, $column); + return static::flatten( + static::handle($items, $column, Order::resolve($groupsOrder)) + ); } - protected static function handle(IlluminateCollection $items, string $column = 'value'): IlluminateCollection + protected static function handle(IC $items, string $column, array $groupsOrder): IC { - return static::flatten( - static::sorting($items, $column) - ); + return static::orderGroups(static::sorting($items, $column), $groupsOrder); } - public static function sorting(IlluminateCollection $items, string $column = 'value'): IlluminateCollection + protected static function orderGroups(IC $items, array $order): IC + { + $result = static::collect(); + + foreach ($order as $key) { + if ($items->has($key)) { + $result->put($key, $items->get($key)); + } + }; + + return $result; + } + + protected static function sorting(IC $items, string $column = 'value'): IC { return $items ->groupBy(static fn (mixed $size) => static::detectGroup( static::resolveValue($size, $column)->toString() ), true) - ->sortKeys() - ->map(static fn (IlluminateCollection $items, int $group) => static::sortByGroup($items, $group, $column)); + ->map(static fn (IC $items, int $group) => static::sortByGroup($items, $group, $column)); } - public static function sortByGroup(IlluminateCollection $items, int $group, string $column): IlluminateCollection + protected static function sortByGroup(IC $items, int $group, string $column): IC { return match ($group) { Group::GROUP_1() => static::sortChars($items, $column), @@ -45,7 +58,7 @@ public static function sortByGroup(IlluminateCollection $items, int $group, stri }; } - public static function sortChars(IlluminateCollection $values, string $column): IlluminateCollection + protected static function sortChars(IC $values, string $column): IC { return $values->groupBy( static fn (mixed $size) => static::resolveValue($size, $column) @@ -55,53 +68,58 @@ public static function sortChars(IlluminateCollection $values, string $column): ) ->sortKeysDesc() ->map( - static fn (IlluminateCollection $values, string $group) => $group === 's' + static fn (IC $values, string $group) => $group === 's' ? static::sortSmallSizes($values, $column) : static::sortArrows($values, $column) ); } - public static function sortSmallSizes(IlluminateCollection $values, string $column): IlluminateCollection + protected static function sortSmallSizes(IC $values, string $column): IC { return $values->sort( SorterService::byArrow($column, -1) ) ->groupBy(static fn (mixed $size) => static::resolveValue($size, $column)->toString(), true) - ->map(static fn (IlluminateCollection $values) => static::sortSpecialChars($values, $column)); + ->map(static fn (IC $values) => static::sortSpecialChars($values, $column)); } - public static function sortSpecialChars(IlluminateCollection $values, string $column): IlluminateCollection + protected static function sortSpecialChars(IC $values, string $column): IC { return $values->sort( SorterService::byChars($column) ); } - public static function sortArrows(IlluminateCollection $values, string $column): IlluminateCollection + protected static function sortArrows(IC $values, string $column): IC { return $values->sort( SorterService::byArrow($column) ); } - public static function sortNumbers(IlluminateCollection $items, string $column): IlluminateCollection + protected static function sortNumbers(IC $items, string $column): IC { return $items->sort( SorterService::byNumbers($column) ); } - public static function detectGroup(string $value): int + protected static function detectGroup(string $value): int { return GroupsDetector::detect($value); } - public static function flatten(IlluminateCollection $items): IlluminateCollection + protected static function collect(): IC + { + return Collection::make(); + } + + protected static function flatten(IC $items): IC { return Collection::flatten($items); } - public static function resolveValue(mixed $value, string $column): Stringable + protected static function resolveValue(mixed $value, string $column): Stringable { return Resolver::value($value, $column); } diff --git a/tests/Sorters/FullGroupsOrderTest.php b/tests/Sorters/FullGroupsOrderTest.php new file mode 100644 index 0000000..ca360fe --- /dev/null +++ b/tests/Sorters/FullGroupsOrderTest.php @@ -0,0 +1,195 @@ + 'XXL', + 101 => '26', + 102 => '28', + 103 => 'XL', + 104 => 'ONE SIZE', + 105 => 'XXS', + 106 => '2', + 107 => '54', + 108 => 'XS', + 109 => 'S', + 110 => 'M', + 111 => 'L', + 112 => 'L/XL', + 113 => 'XL/2XL', + 114 => 'XS/S', + 115 => 'M/L', + 116 => '80B', + 117 => '75C', + 118 => '70B', + 119 => '75A', + 120 => '75B', + 121 => '36', + 122 => 'XXS-XS', + 123 => '37', + 124 => '38', + 125 => '39', + 126 => '40', + 127 => 'S/M', + 128 => '40х38х19 см', + 129 => '70C', + 130 => '44-46', + 131 => 'some', + 132 => '1', + 133 => '30', + 134 => '32', + 135 => '34', + 136 => '44/46', + 137 => 'XXS/XS', + 138 => 'XXS', + 139 => '52-56', + 140 => '102-104', + 141 => '102-106', + 142 => '102/106', + 143 => '106', + 145 => '110-112', + 144 => '110-114', + 146 => '90/94', + 147 => '94-98', + 148 => '98-102', + 149 => '21', + 150 => '3', + 151 => '40х38х19 sm', + 152 => '40х37х19 см', + 153 => '40х37х20 см', + 154 => '40х38х15 см', + 155 => '41х38х15 см', + 156 => '39х38х15 см', + ]; + + private array $expected = [ + // 3 + 118 => '70B', + 129 => '70C', + 119 => '75A', + 120 => '75B', + 117 => '75C', + 116 => '80B', + // 5 + 104 => 'ONE SIZE', + 131 => 'some', + // 4 + 156 => '39х38х15 см', + 152 => '40х37х19 см', + 153 => '40х37х20 см', + 154 => '40х38х15 см', + 151 => '40х38х19 sm', + 128 => '40х38х19 см', + 155 => '41х38х15 см', + // 2 + 132 => '1', + 106 => '2', + 150 => '3', + 149 => '21', + 101 => '26', + 102 => '28', + 133 => '30', + 134 => '32', + 135 => '34', + 121 => '36', + 123 => '37', + 124 => '38', + 125 => '39', + 126 => '40', + 130 => '44-46', + 136 => '44/46', + 139 => '52-56', + 107 => '54', + 146 => '90/94', + 147 => '94-98', + 148 => '98-102', + 140 => '102-104', + 141 => '102-106', + 142 => '102/106', + 143 => '106', + 145 => '110-112', + 144 => '110-114', + // 1 + 105 => 'XXS', + 138 => 'XXS', + 137 => 'XXS/XS', + 122 => 'XXS-XS', + 108 => 'XS', + 114 => 'XS/S', + 109 => 'S', + 127 => 'S/M', + 110 => 'M', + 115 => 'M/L', + 111 => 'L', + 112 => 'L/XL', + 103 => 'XL', + 113 => 'XL/2XL', + 100 => 'XXL', + ]; + + private array $groupsOrder = [ + Group::GROUP_3, + Group::GROUP_5, + Group::GROUP_4, + Group::GROUP_2, + Group::GROUP_1, + ]; + + public function testArray(): void + { + $this->assertSame( + $this->expected, + Sorter::sort(collect($this->values), groupsOrder: $this->groupsOrder)->toArray() + ); + } + + public function testObjects(): void + { + $items = collect($this->values)->map(fn (string $value, int $key) => (object) [ + 'id' => $key, + 'value' => $value, + 'active' => true, + ]); + + $this->assertSame( + $this->expected, + Sorter::sort($items, groupsOrder: $this->groupsOrder)->pluck('value', 'id')->toArray() + ); + } + + public function testCustomColumn(): void + { + $items = collect($this->values)->map(fn (string $value, int $key) => (object) [ + 'id' => $key, + 'some' => $value, + ]); + + $this->assertSame( + $this->expected, + Sorter::sort($items, 'some', $this->groupsOrder)->pluck('some', 'id')->toArray() + ); + } + + public function testWithSaveKeys(): void + { + $values = [ + 840 => 'XL', + 506 => 'XS', + ]; + + $expected = [ + 506 => 'XS', + 840 => 'XL', + ]; + + $this->assertSame($expected, Sorter::sort(collect($values), groupsOrder: $this->groupsOrder)->toArray()); + } +} diff --git a/tests/Sorters/PartialGroupsOrderTest.php b/tests/Sorters/PartialGroupsOrderTest.php new file mode 100644 index 0000000..5347c9a --- /dev/null +++ b/tests/Sorters/PartialGroupsOrderTest.php @@ -0,0 +1,192 @@ + 'XXL', + 101 => '26', + 102 => '28', + 103 => 'XL', + 104 => 'ONE SIZE', + 105 => 'XXS', + 106 => '2', + 107 => '54', + 108 => 'XS', + 109 => 'S', + 110 => 'M', + 111 => 'L', + 112 => 'L/XL', + 113 => 'XL/2XL', + 114 => 'XS/S', + 115 => 'M/L', + 116 => '80B', + 117 => '75C', + 118 => '70B', + 119 => '75A', + 120 => '75B', + 121 => '36', + 122 => 'XXS-XS', + 123 => '37', + 124 => '38', + 125 => '39', + 126 => '40', + 127 => 'S/M', + 128 => '40х38х19 см', + 129 => '70C', + 130 => '44-46', + 131 => 'some', + 132 => '1', + 133 => '30', + 134 => '32', + 135 => '34', + 136 => '44/46', + 137 => 'XXS/XS', + 138 => 'XXS', + 139 => '52-56', + 140 => '102-104', + 141 => '102-106', + 142 => '102/106', + 143 => '106', + 145 => '110-112', + 144 => '110-114', + 146 => '90/94', + 147 => '94-98', + 148 => '98-102', + 149 => '21', + 150 => '3', + 151 => '40х38х19 sm', + 152 => '40х37х19 см', + 153 => '40х37х20 см', + 154 => '40х38х15 см', + 155 => '41х38х15 см', + 156 => '39х38х15 см', + ]; + + private array $expected = [ + // 3 + 118 => '70B', + 129 => '70C', + 119 => '75A', + 120 => '75B', + 117 => '75C', + 116 => '80B', + // 5 + 104 => 'ONE SIZE', + 131 => 'some', + // 1 + 105 => 'XXS', + 138 => 'XXS', + 137 => 'XXS/XS', + 122 => 'XXS-XS', + 108 => 'XS', + 114 => 'XS/S', + 109 => 'S', + 127 => 'S/M', + 110 => 'M', + 115 => 'M/L', + 111 => 'L', + 112 => 'L/XL', + 103 => 'XL', + 113 => 'XL/2XL', + 100 => 'XXL', + // 2 + 132 => '1', + 106 => '2', + 150 => '3', + 149 => '21', + 101 => '26', + 102 => '28', + 133 => '30', + 134 => '32', + 135 => '34', + 121 => '36', + 123 => '37', + 124 => '38', + 125 => '39', + 126 => '40', + 130 => '44-46', + 136 => '44/46', + 139 => '52-56', + 107 => '54', + 146 => '90/94', + 147 => '94-98', + 148 => '98-102', + 140 => '102-104', + 141 => '102-106', + 142 => '102/106', + 143 => '106', + 145 => '110-112', + 144 => '110-114', + // 4 + 156 => '39х38х15 см', + 152 => '40х37х19 см', + 153 => '40х37х20 см', + 154 => '40х38х15 см', + 151 => '40х38х19 sm', + 128 => '40х38х19 см', + 155 => '41х38х15 см', + ]; + + private array $groupsOrder = [ + Group::GROUP_3, + Group::GROUP_5, + ]; + + public function testArray(): void + { + $this->assertSame( + $this->expected, + Sorter::sort(collect($this->values), groupsOrder: $this->groupsOrder)->toArray() + ); + } + + public function testObjects(): void + { + $items = collect($this->values)->map(fn (string $value, int $key) => (object) [ + 'id' => $key, + 'value' => $value, + 'active' => true, + ]); + + $this->assertSame( + $this->expected, + Sorter::sort($items, groupsOrder: $this->groupsOrder)->pluck('value', 'id')->toArray() + ); + } + + public function testCustomColumn(): void + { + $items = collect($this->values)->map(fn (string $value, int $key) => (object) [ + 'id' => $key, + 'some' => $value, + ]); + + $this->assertSame( + $this->expected, + Sorter::sort($items, 'some', $this->groupsOrder)->pluck('some', 'id')->toArray() + ); + } + + public function testWithSaveKeys(): void + { + $values = [ + 840 => 'XL', + 506 => 'XS', + ]; + + $expected = [ + 506 => 'XS', + 840 => 'XL', + ]; + + $this->assertSame($expected, Sorter::sort(collect($values), groupsOrder: $this->groupsOrder)->toArray()); + } +}