Issues (25)

src/ArraySorter.php (5 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Arrays;
6
7
use Closure;
8
use InvalidArgumentException;
9
10
use function is_array;
11
12
final class ArraySorter
13
{
14
    /**
15
     * Sorts an array of objects or arrays (with the same structure) by one or several keys.
16
     *
17
     * For example:
18
     *
19
     * ```php
20
     * $data = [
21
     *     ['age' => 30, 'name' => 'Alexander'],
22
     *     ['age' => 30, 'name' => 'Brian'],
23
     *     ['age' => 19, 'name' => 'Barney'],
24
     * ];
25
     * ArraySorter::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);
26
     * ```
27
     *
28
     * After sorting we'll get the following in `$data`:
29
     *
30
     * ```php
31
     * [
32
     *     ['age' => 19, 'name' => 'Barney'],
33
     *     ['age' => 30, 'name' => 'Brian'],
34
     *     ['age' => 30, 'name' => 'Alexander'],
35
     * ];
36
     * ```
37
     *
38
     * @param array<array-key, array|object> $array The array to be sorted. The array will be modified after calling
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array|object> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array|object>.
Loading history...
39
     * this method.
40
     * @param array<array-key, Closure|string>|Closure|string $key The key(s) to be sorted by. This refers to a key
41
     * name of the sub-array elements, a property name of the objects, or an anonymous function returning the values
42
     * for comparison purpose. The anonymous function signature should be: `function($item)`.
43
     * To sort by multiple keys, provide an array of keys here.
44
     * @param array<array-key, int>|int $direction The sorting direction. It can be either `SORT_ASC` or `SORT_DESC`.
45
     * When sorting by multiple keys with different sorting directions, use an array of sorting directions.
46
     * @param array<array-key, int>|int $sortFlag The PHP sort flag. Valid values include
47
     * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, `SORT_LOCALE_STRING`, `SORT_NATURAL` and `SORT_FLAG_CASE`.
48
     * Please refer to [PHP manual](https://php.net/manual/en/function.sort.php)
49
     * for more details. When sorting by multiple keys with different sort flags, use an array of sort flags.
50
     *
51
     * @throws InvalidArgumentException If the `$direction` or `$sortFlag` parameters do not have
52
     * correct number of elements as that of $key.`
53
     */
54 6
    public static function multisort(
55
        array &$array,
56
        array|Closure|string $key,
57
        array|int $direction = SORT_ASC,
58
        array|int $sortFlag = SORT_REGULAR
59
    ): void {
60 6
        $keys = self::getKeys($array, $key);
61 6
        if (empty($keys)) {
62 1
            return;
63
        }
64
65 6
        $n = count($keys);
66 6
        if (is_scalar($direction)) {
67 3
            $direction = array_fill(0, $n, $direction);
68 3
        } elseif (count($direction) !== $n) {
69 1
            throw new InvalidArgumentException('The length of $direction parameter must be the same as that of $keys.');
70
        }
71
72 5
        if (is_scalar($sortFlag)) {
73 4
            $sortFlag = array_fill(0, $n, $sortFlag);
74 2
        } elseif (count($sortFlag) !== $n) {
75 1
            throw new InvalidArgumentException('The length of $sortFlag parameter must be the same as that of $keys.');
76
        }
77
78 4
        $_args = self::getArguments($array, $keys, $direction, $sortFlag);
79
80
        /** @psalm-suppress UnsupportedReferenceUsage */
81 4
        $_args[] = &$array;
82
83
        /** @psalm-suppress MixedArgument */
84 4
        array_multisort(...$_args);
85
    }
86
87
    /**
88
     * Get keys for get arguments.
89
     *
90
     * @param array<array-key, array|object> $array The array to be sorted.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array|object> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array|object>.
Loading history...
91
     * @param array<array-key, Closure|string>|Closure|string $key The keys to be sorted by. This refers to a key name
92
     * of the sub-array elements, a property name of the objects, or an anonymous function returning the values for
93
     * comparison purpose. The anonymous function signature should be: `function($item)`.
94
     * To sort by multiple keys, provide an array of keys here.
95
     *
96
     * @return array<array-key, Closure|string> The keys.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, Closure|string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, Closure|string>.
Loading history...
97
     */
98 6
    private static function getKeys(array $array, array|Closure|string $key): array
99
    {
100 6
        $keys = is_array($key) ? $key : [$key];
0 ignored issues
show
The condition is_array($key) is always true.
Loading history...
101 6
        if (empty($keys) || empty($array)) {
102 1
            return [];
103
        }
104
105 6
        return $keys;
106
    }
107
108
    /**
109
     * Get arguments for multisort.
110
     *
111
     * @param array<array-key, array|object> $array The array to be sorted.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array|object> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array|object>.
Loading history...
112
     * @param array<array-key, Closure|string> $keys Array of keys.
113
     * @param array<array-key, int> $direction Array of sorting directions.
114
     * @param array<array-key, int> $sortFlags Array of sort flags.
115
     *
116
     * @return array The arguments.
117
     */
118 4
    private static function getArguments(array $array, array $keys, array $direction, array $sortFlags): array
119
    {
120 4
        $args = [];
121 4
        foreach ($keys as $i => $iKey) {
122 4
            $flag = $sortFlags[$i];
123 4
            $args[] = ArrayHelper::getColumn($array, $iKey);
124 4
            $args[] = $direction[$i];
125 4
            $args[] = $flag;
126
        }
127
128
        // This fix is used for cases when main sorting specified by columns has equal values.
129
        // Without it will lead to Fatal Error: Nesting level too deep - recursive dependency?
130 4
        $args[] = range(1, count($array));
131 4
        $args[] = SORT_ASC;
132 4
        $args[] = SORT_NUMERIC;
133
134 4
        return $args;
135
    }
136
}
137