Passed
Push — master ( fcb5ef...e65bc8 )
by
unknown
13:57 queued 18s
created

ChooseRowsEtc::chooseCols()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 10
c 1
b 0
f 0
cc 3
nc 4
nop 2
crap 3
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
    /**
84
     * @param mixed[] $array
85
     *
86
     * @return mixed[]|string
87
     */
88 19
    private static function dropRows(array $array, mixed $offset): array|string
89
    {
90 19
        if ($offset === null) {
91 7
            return $array;
92
        }
93 19
        if (!is_numeric($offset)) {
94 2
            return ExcelError::VALUE();
95
        }
96 18
        $offset = (int) $offset;
97 18
        $count = count($array);
98 18
        if (abs($offset) >= $count) {
99
            // In theory, this should be #CALC!, but Excel treats
100
            // #CALC! as corrupt, and it's not worth figuring out why
101 6
            return ExcelError::VALUE();
102
        }
103 13
        if ($offset === 0) {
104 2
            return $array;
105
        }
106 12
        if ($offset > 0) {
107 9
            return array_slice($array, $offset);
108
        }
109
110 7
        return array_slice($array, 0, $count + $offset);
111
    }
112
113
    /**
114
     * DROP.
115
     *
116
     * @param mixed $input expect two-dimensional array
117
     *
118
     * @return mixed[]|string
119
     */
120 19
    public static function drop(mixed $input, mixed $rows = null, mixed $columns = null): array|string
121
    {
122 19
        if (!is_array($input)) {
123 1
            $input = [[$input]];
124
        }
125 19
        $inputArray = []; // no row 0
126 19
        foreach ($input as $inputRow) {
127 19
            $inputArray[] = self::arrayValues($inputRow);
128
        }
129 19
        $outputArray1 = self::dropRows($inputArray, $rows);
130 19
        if (is_string($outputArray1)) {
131 4
            return $outputArray1;
132
        }
133 15
        $outputArray2 = self::transpose($outputArray1);
134 15
        $outputArray3 = self::dropRows($outputArray2, $columns);
135 15
        if (is_string($outputArray3)) {
136 4
            return $outputArray3;
137
        }
138
139 11
        return self::transpose($outputArray3);
140
    }
141
142
    /**
143
     * @param mixed[] $array
144
     *
145
     * @return mixed[]|string
146
     */
147 19
    private static function takeRows(array $array, mixed $offset): array|string
148
    {
149 19
        if ($offset === null) {
150 9
            return $array;
151
        }
152 19
        if (!is_numeric($offset)) {
153 2
            return ExcelError::VALUE();
154
        }
155 18
        $offset = (int) $offset;
156 18
        if ($offset === 0) {
157
            // should be #CALC! - see above
158 2
            return ExcelError::VALUE();
159
        }
160 17
        $count = count($array);
161 17
        if (abs($offset) >= $count) {
162 7
            return $array;
163
        }
164 12
        if ($offset > 0) {
165 8
            return array_slice($array, 0, $offset);
166
        }
167
168 7
        return array_slice($array, $count + $offset);
169
    }
170
171
    /**
172
     * TAKE.
173
     *
174
     * @param mixed $input expecting two-dimensional array
175
     *
176
     * @return mixed[]|string
177
     */
178 19
    public static function take(mixed $input, mixed $rows, mixed $columns = null): array|string
179
    {
180 19
        if (!is_array($input)) {
181 1
            $input = [[$input]];
182
        }
183 19
        if ($rows === null && $columns === null) {
184
            return $input;
185
        }
186 19
        $inputArray = [];
187 19
        foreach ($input as $inputRow) {
188 19
            $inputArray[] = self::arrayValues($inputRow);
189
        }
190 19
        $outputArray1 = self::takeRows($inputArray, $rows);
191 19
        if (is_string($outputArray1)) {
192 2
            return $outputArray1;
193
        }
194 17
        $outputArray2 = self::transpose($outputArray1);
195 17
        $outputArray3 = self::takeRows($outputArray2, $columns);
196 17
        if (is_string($outputArray3)) {
197 2
            return $outputArray3;
198
        }
199
200 15
        return self::transpose($outputArray3);
201
    }
202
203
    /**
204
     * EXPAND.
205
     *
206
     * @param mixed $input expecting two-dimensional array
207
     *
208
     * @return mixed[]|string
209
     */
210 10
    public static function expand(mixed $input, mixed $rows, mixed $columns = null, mixed $pad = '#N/A'): array|string
211
    {
212 10
        if (!is_array($input)) {
213 1
            $input = [[$input]];
214
        }
215 10
        if ($rows === null && $columns === null) {
216
            return $input;
217
        }
218 10
        $numRows = count($input);
219 10
        $rows ??= $numRows;
220 10
        if (!is_numeric($rows)) {
221 1
            return ExcelError::VALUE();
222
        }
223 9
        $rows = (int) $rows;
224 9
        if ($rows < count($input)) {
225 2
            return ExcelError::VALUE();
226
        }
227 7
        $numCols = 0;
228 7
        foreach ($input as $inputRow) {
229 7
            $numCols = max($numCols, is_array($inputRow) ? count($inputRow) : 1);
230
        }
231 7
        $columns ??= $numCols;
232 7
        if (!is_numeric($columns)) {
233
            return ExcelError::VALUE();
234
        }
235 7
        $columns = (int) $columns;
236 7
        if ($columns < $numCols) {
237 1
            return ExcelError::VALUE();
238
        }
239 6
        $inputArray = [];
240 6
        foreach ($input as $inputRow) {
241 6
            $inputArray[] = array_pad(self::arrayValues($inputRow), $columns, $pad);
242
        }
243 6
        $outputArray = [];
244 6
        $padRow = array_pad([], $columns, $pad);
245 6
        for ($count = 0; $count < $rows; ++$count) {
246 6
            $outputArray[] = ($count >= $numRows) ? $padRow : $inputArray[$count];
247
        }
248
249 6
        return $outputArray;
250
    }
251
}
252