Passed
Pull Request — master (#4286)
by Owen
15:27
created

ChooseRowsEtc::take()   B

Complexity

Conditions 7
Paths 14

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7.0119

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
dl 0
loc 23
ccs 15
cts 16
cp 0.9375
rs 8.8333
c 1
b 0
f 0
cc 7
nc 14
nop 3
crap 7.0119
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
6
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
7
8
class ChooseRowsEtc
9
{
10
    /**
11
     * Transpose 2-dimensional array.
12
     * See https://stackoverflow.com/questions/797251/transposing-multidimensional-arrays-in-php
13
     * especially the comment from user17994717.
14
     *
15
     * @param mixed[] $array
16
     *
17
     * @return mixed[]
18
     */
19 49
    public static function transpose(array $array): array
20
    {
21 49
        return empty($array) ? [] : (array_map((count($array) === 1) ? (fn ($x) => [$x]) : null, ...$array)); // @phpstan-ignore-line
22
    }
23
24
    /** @return mixed[] */
25 77
    private static function arrayValues(mixed $array): array
26
    {
27 77
        return is_array($array) ? array_values($array) : [$array];
28
    }
29
30
    /**
31
     * CHOOSECOLS.
32
     *
33
     * @param mixed $input expecting two-dimensional array
34
     *
35
     * @return mixed[]|string
36
     */
37 17
    public static function chooseCols(mixed $input, mixed ...$args): array|string
38
    {
39 17
        if (!is_array($input)) {
40 1
            $input = [[$input]];
41
        }
42 17
        $retval = self::chooseRows(self::transpose($input), ...$args);
43
44 17
        return is_array($retval) ? self::transpose($retval) : $retval;
45
    }
46
47
    /**
48
     * CHOOSEROWS.
49
     *
50
     * @param mixed $input expecting two-dimensional array
51
     *
52
     * @return mixed[]|string
53
     */
54 33
    public static function chooseRows(mixed $input, mixed ...$args): array|string
55
    {
56 33
        if (!is_array($input)) {
57 1
            $input = [[$input]];
58
        }
59 33
        $inputArray = [[]]; // no row 0
60 33
        $numRows = 0;
61 33
        foreach ($input as $inputRow) {
62 33
            $inputArray[] = self::arrayValues($inputRow);
63 33
            ++$numRows;
64
        }
65 33
        $outputArray = [];
66 33
        foreach (Functions::flattenArray2(...$args) as $arg) {
67 33
            if (!is_numeric($arg)) {
68 2
                return ExcelError::VALUE();
69
            }
70 31
            $index = (int) $arg;
71 31
            if ($index < 0) {
72 12
                $index += $numRows + 1;
73
            }
74 31
            if ($index <= 0 || $index > $numRows) {
75 6
                return ExcelError::VALUE();
76
            }
77 27
            $outputArray[] = $inputArray[$index];
78
        }
79
80 25
        return $outputArray;
81
    }
82
83 19
    private static function dropRows(array $array, mixed $offset): array|string
84
    {
85 19
        if ($offset === null) {
86 7
            return $array;
87
        }
88 19
        if (!is_numeric($offset)) {
89 2
            return ExcelError::VALUE();
90
        }
91 18
        $offset = (int) $offset;
92 18
        $count = count($array);
93 18
        if (abs($offset) >= $count) {
94
            // In theory, this should be #CALC!, but Excel treats
95
            // #CALC! as corrupt, and it's not worth figuring out why
96 6
            return ExcelError::VALUE();
97
        }
98 13
        if ($offset === 0) {
99 2
            return $array;
100
        }
101 12
        if ($offset > 0) {
102 9
            return array_slice($array, $offset);
103
        }
104
105 7
        return array_slice($array, 0, $count + $offset);
106
    }
107
108
    /**
109
     * DROP.
110
     *
111
     * @param mixed $input expect two-dimensional array
112
     *
113
     * @return mixed[]|string
114
     */
115 19
    public static function drop(mixed $input, mixed $rows = null, mixed $columns = null): array|string
116
    {
117 19
        if (!is_array($input)) {
118 1
            $input = [[$input]];
119
        }
120 19
        $inputArray = []; // no row 0
121 19
        foreach ($input as $inputRow) {
122 19
            $inputArray[] = self::arrayValues($inputRow);
123
        }
124 19
        $outputArray1 = self::dropRows($inputArray, $rows);
125 19
        if (is_string($outputArray1)) {
126 4
            return $outputArray1;
127
        }
128 15
        $outputArray2 = self::transpose($outputArray1);
129 15
        $outputArray3 = self::dropRows($outputArray2, $columns);
130 15
        if (is_string($outputArray3)) {
131 4
            return $outputArray3;
132
        }
133
134 11
        return self::transpose($outputArray3);
135
    }
136
137 19
    private static function takeRows(array $array, mixed $offset): array|string
138
    {
139 19
        if ($offset === null) {
140 9
            return $array;
141
        }
142 19
        if (!is_numeric($offset)) {
143 2
            return ExcelError::VALUE();
144
        }
145 18
        $offset = (int) $offset;
146 18
        if ($offset === 0) {
147
            // should be #CALC! - see above
148 2
            return ExcelError::VALUE();
149
        }
150 17
        $count = count($array);
151 17
        if (abs($offset) >= $count) {
152 7
            return $array;
153
        }
154 12
        if ($offset > 0) {
155 8
            return array_slice($array, 0, $offset);
156
        }
157
158 7
        return array_slice($array, $count + $offset);
159
    }
160
161
    /**
162
     * TAKE.
163
     *
164
     * @param mixed $input expecting two-dimensional array
165
     *
166
     * @return mixed[]|string
167
     */
168 19
    public static function take(mixed $input, mixed $rows, mixed $columns = null): array|string
169
    {
170 19
        if (!is_array($input)) {
171 1
            $input = [[$input]];
172
        }
173 19
        if ($rows === null && $columns === null) {
174
            return $input;
175
        }
176 19
        $inputArray = [];
177 19
        foreach ($input as $inputRow) {
178 19
            $inputArray[] = self::arrayValues($inputRow);
179
        }
180 19
        $outputArray1 = self::takeRows($inputArray, $rows);
181 19
        if (is_string($outputArray1)) {
182 2
            return $outputArray1;
183
        }
184 17
        $outputArray2 = self::transpose($outputArray1);
185 17
        $outputArray3 = self::takeRows($outputArray2, $columns);
186 17
        if (is_string($outputArray3)) {
187 2
            return $outputArray3;
188
        }
189
190 15
        return self::transpose($outputArray3);
191
    }
192
193
    /**
194
     * EXPAND.
195
     *
196
     * @param mixed $input expecting two-dimensional array
197
     *
198
     * @return mixed[]|string
199
     */
200 10
    public static function expand(mixed $input, mixed $rows, mixed $columns = null, mixed $pad = '#N/A'): array|string
201
    {
202 10
        if (!is_array($input)) {
203 1
            $input = [[$input]];
204
        }
205 10
        if ($rows === null && $columns === null) {
206
            return $input;
207
        }
208 10
        $numRows = count($input);
209 10
        $rows ??= $numRows;
210 10
        if (!is_numeric($rows)) {
211 1
            return ExcelError::VALUE();
212
        }
213 9
        $rows = (int) $rows;
214 9
        if ($rows < count($input)) {
215 2
            return ExcelError::VALUE();
216
        }
217 7
        $numCols = 0;
218 7
        foreach ($input as $inputRow) {
219 7
            $numCols = max($numCols, is_array($inputRow) ? count($inputRow) : 1);
220
        }
221 7
        $columns ??= $numCols;
222 7
        if (!is_numeric($columns)) {
223
            return ExcelError::VALUE();
224
        }
225 7
        $columns = (int) $columns;
226 7
        if ($columns < $numCols) {
227 1
            return ExcelError::VALUE();
228
        }
229 6
        $inputArray = [];
230 6
        foreach ($input as $inputRow) {
231 6
            $inputArray[] = array_pad(self::arrayValues($inputRow), $columns, $pad);
232
        }
233 6
        $outputArray = [];
234 6
        $padRow = array_pad([], $columns, $pad);
235 6
        for ($count = 0; $count < $rows; ++$count) {
236 6
            $outputArray[] = ($count >= $numRows) ? $padRow : $inputArray[$count];
237
        }
238
239 6
        return $outputArray;
240
    }
241
}
242