Skip to content

Commit bd792ec

Browse files
authored
Merge pull request #4492 from oleibman/vstack
VSTACK and HSTACK
2 parents 1825c74 + e83eae9 commit bd792ec

File tree

8 files changed

+369
-10
lines changed

8 files changed

+369
-10
lines changed

docs/references/function-list-by-category.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ FORMULATEXT | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Formu
251251
GETPIVOTDATA | **Not yet Implemented**
252252
GROUPBY | **Not yet Implemented**
253253
HLOOKUP | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup::lookup
254+
HSTACK | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Hstack::hstack
254255
HYPERLINK | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Hyperlink::set
255256
INDEX | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix::index
256257
INDIRECT | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Indirect::INDIRECT
@@ -266,6 +267,7 @@ TAKE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Choos
266267
TRANSPOSE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix::transpose
267268
UNIQUE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Unique::unique
268269
VLOOKUP | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup::lookup
270+
VSTACK | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Vstack::vstack
269271
XLOOKUP | **Not yet Implemented**
270272
XMATCH | **Not yet Implemented**
271273

@@ -308,7 +310,6 @@ FLOOR | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Floor:
308310
FLOOR.MATH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Floor::math
309311
FLOOR.PRECISE | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Floor::precise
310312
GCD | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Gcd::evaluate
311-
HSTACK | **Not yet Implemented**
312313
INT | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\IntClass::evaluate
313314
ISO.CEILING | **Not yet Implemented**
314315
LCM | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Lcm::evaluate
@@ -360,7 +361,6 @@ TANH | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trig\T
360361
TOCOL | **Not yet Implemented**
361362
TOROW | **Not yet Implemented**
362363
TRUNC | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trunc::evaluate
363-
VSTACK | **Not yet Implemented**
364364
WRAPCOLS | **Not yet Implemented**
365365
WRAPROWS | **Not yet Implemented**
366366

docs/references/function-list-by-name-compact.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ HEX2DEC | ENGINEERING | Engineering\ConvertHex::toDec
259259
HEX2OCT | ENGINEERING | Engineering\ConvertHex::toOctal
260260
HLOOKUP | LOOKUP_AND_REFERENCE | LookupRef\HLookup::lookup
261261
HOUR | DATE_AND_TIME | DateTimeExcel\TimeParts::hour
262-
HSTACK | MATH_AND_TRIG | **Not yet Implemented**
262+
HSTACK | LOOKUP_AND_REFERENCE | LookupRef\Hstack::hstack
263263
HYPERLINK | LOOKUP_AND_REFERENCE | LookupRef\Hyperlink::set
264264
HYPGEOM.DIST | STATISTICAL | **Not yet Implemented**
265265
HYPGEOMDIST | STATISTICAL | Statistical\Distributions\HyperGeometric::distribution
@@ -619,7 +619,7 @@ VARP | STATISTICAL | Statistical\Variances::VARP
619619
VARPA | STATISTICAL | Statistical\Variances::VARPA
620620
VDB | FINANCIAL | **Not yet Implemented**
621621
VLOOKUP | LOOKUP_AND_REFERENCE | LookupRef\VLookup::lookup
622-
VSTACK | MATH_AND_TRIG | **Not yet Implemented**
622+
VSTACK | LOOKUP_AND_REFERENCE | LookupRef\Vstack::vstack
623623

624624
## W
625625

docs/references/function-list-by-name.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ HEX2DEC | CATEGORY_ENGINEERING | \PhpOffice\PhpSpread
255255
HEX2OCT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering\ConvertHex::toOctal
256256
HLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup::lookup
257257
HOUR | CATEGORY_DATE_AND_TIME | \PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\TimeParts::hour
258-
HSTACK | CATEGORY_MATH_AND_TRIG | **Not yet Implemented**
258+
HSTACK | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Hstack::hstack
259259
HYPERLINK | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Hyperlink::set
260260
HYPGEOM.DIST | CATEGORY_STATISTICAL | **Not yet Implemented**
261261
HYPGEOMDIST | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions\HyperGeometric::distribution
@@ -615,7 +615,7 @@ VARP | CATEGORY_STATISTICAL | \PhpOffice\PhpSpread
615615
VARPA | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances::VARPA
616616
VDB | CATEGORY_FINANCIAL | **Not yet Implemented**
617617
VLOOKUP | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup::lookup
618-
VSTACK | CATEGORY_MATH_AND_TRIG | **Not yet Implemented**
618+
VSTACK | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Vstack::vstack
619619

620620
## W
621621

src/PhpSpreadsheet/Calculation/FunctionArray.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,8 +1106,8 @@ class FunctionArray extends CalculationBase
11061106
'argumentCount' => '1',
11071107
],
11081108
'HSTACK' => [
1109-
'category' => Category::CATEGORY_MATH_AND_TRIG,
1110-
'functionCall' => [Functions::class, 'DUMMY'],
1109+
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
1110+
'functionCall' => [LookupRef\Hstack::class, 'hstack'],
11111111
'argumentCount' => '1+',
11121112
],
11131113
'HYPERLINK' => [
@@ -2569,8 +2569,8 @@ class FunctionArray extends CalculationBase
25692569
'argumentCount' => '3,4',
25702570
],
25712571
'VSTACK' => [
2572-
'category' => Category::CATEGORY_MATH_AND_TRIG,
2573-
'functionCall' => [Functions::class, 'DUMMY'],
2572+
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
2573+
'functionCall' => [LookupRef\Vstack::class, 'vstack'],
25742574
'argumentCount' => '1+',
25752575
],
25762576
'WEBSERVICE' => [
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 Hstack
9+
{
10+
/**
11+
* Excel function HSTACK.
12+
*
13+
* @return mixed[]|string
14+
*/
15+
public static function hstack(mixed ...$inputData): array|string
16+
{
17+
$maxRow = 0;
18+
foreach ($inputData as $matrix) {
19+
if (!is_array($matrix)) {
20+
$count = 1;
21+
} else {
22+
$count = count($matrix);
23+
}
24+
$maxRow = max($maxRow, $count);
25+
}
26+
/** @var mixed[] $inputData */
27+
foreach ($inputData as &$matrix) {
28+
if (!is_array($matrix)) {
29+
$matrix = [$matrix];
30+
}
31+
$rows = count($matrix);
32+
$reset = reset($matrix);
33+
$columns = is_array($reset) ? count($reset) : 1;
34+
while ($maxRow > $rows) {
35+
$matrix[] = array_pad([], $columns, ExcelError::NA());
36+
++$rows;
37+
}
38+
}
39+
40+
$transpose = array_map(null, ...$inputData); //* @phpstan-ignore-line
41+
$returnMatrix = [];
42+
foreach ($transpose as $array) {
43+
$returnMatrix[] = Functions::flattenArray($array);
44+
}
45+
46+
return $returnMatrix;
47+
}
48+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
6+
7+
class Vstack
8+
{
9+
/**
10+
* Excel function VSTACK.
11+
*
12+
* @return mixed[]
13+
*/
14+
public static function vstack(mixed ...$inputData): array|string
15+
{
16+
$returnMatrix = [];
17+
18+
$columns = 0;
19+
foreach ($inputData as $matrix) {
20+
if (!is_array($matrix)) {
21+
$count = 1;
22+
} else {
23+
$count = count(reset($matrix)); //* @phpstan-ignore-line
24+
}
25+
$columns = max($columns, $count);
26+
}
27+
28+
foreach ($inputData as $matrix) {
29+
if (!is_array($matrix)) {
30+
$matrix = [$matrix];
31+
}
32+
foreach ($matrix as $row) {
33+
if (!is_array($row)) {
34+
$row = [$row];
35+
}
36+
$returnMatrix[] = array_values(array_pad($row, $columns, ExcelError::NA()));
37+
}
38+
}
39+
40+
return $returnMatrix;
41+
}
42+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
6+
7+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
8+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
9+
10+
/**
11+
* Many of these tests are derived from
12+
* https://exceljet.net/functions/hstack-function.
13+
*/
14+
class HStackTest extends AllSetupTeardown
15+
{
16+
public static function testHstack1(): void
17+
{
18+
$spreadsheet = new Spreadsheet();
19+
Calculation::getInstance($spreadsheet)
20+
->setInstanceArrayReturnType(
21+
Calculation::RETURN_ARRAY_AS_ARRAY
22+
);
23+
$sheet = $spreadsheet->getActiveSheet();
24+
$b4tof14 = [
25+
['A', null, 'B', null, 'C'],
26+
[12, null, 7, null, 12],
27+
[9, null, 13, null, 10],
28+
[10, null, 5, null, 11],
29+
[11, null, 13, null, 6],
30+
[8, null, 12, null, 7],
31+
[12, null, 11, null, 15],
32+
[9, null, 10, null, 6],
33+
[10, null, 15, null, 5],
34+
[11, null, 9, null, 14],
35+
[6, null, 13, null, 11],
36+
];
37+
$sheet->fromArray($b4tof14, null, 'B4', true);
38+
$sheet->setCellValue('H4', '=HSTACK(B4:B14,D4:D14,F4:F14)');
39+
$expected = [
40+
['A', 'B', 'C'],
41+
[12, 7, 12],
42+
[9, 13, 10],
43+
[10, 5, 11],
44+
[11, 13, 6],
45+
[8, 12, 7],
46+
[12, 11, 15],
47+
[9, 10, 6],
48+
[10, 15, 5],
49+
[11, 9, 14],
50+
[6, 13, 11],
51+
];
52+
self::assertSame($expected, $sheet->getCell('H4')->getCalculatedValue());
53+
54+
$sheet->setCellValue('K4', '=HSTACK(B4:B14,D4:D12,F4:F14)');
55+
$expected = [
56+
['A', 'B', 'C'],
57+
[12, 7, 12],
58+
[9, 13, 10],
59+
[10, 5, 11],
60+
[11, 13, 6],
61+
[8, 12, 7],
62+
[12, 11, 15],
63+
[9, 10, 6],
64+
[10, 15, 5],
65+
[11, '#N/A', 14],
66+
[6, '#N/A', 11],
67+
];
68+
self::assertSame($expected, $sheet->getCell('K4')->getCalculatedValue(), 'one short column');
69+
70+
$sheet->setCellValue('R4', '=IFERROR(HSTACK(B4:B14,D4:D12,F4:F14),"")');
71+
$expected = [
72+
['A', 'B', 'C'],
73+
[12, 7, 12],
74+
[9, 13, 10],
75+
[10, 5, 11],
76+
[11, 13, 6],
77+
[8, 12, 7],
78+
[12, 11, 15],
79+
[9, 10, 6],
80+
[10, 15, 5],
81+
[11, '', 14],
82+
[6, '', 11],
83+
];
84+
self::assertSame($expected, $sheet->getCell('R4')->getCalculatedValue(), 'one short column with null string instead of N/A');
85+
86+
$sheet->setCellValue('N4', '=HSTACK(B5,H6:J6)');
87+
$expected = [
88+
[12, 9, 13, 10],
89+
];
90+
self::assertSame($expected, $sheet->getCell('N4')->getCalculatedValue(), 'one single-cell arg');
91+
92+
$spreadsheet->disconnectWorksheets();
93+
}
94+
}

0 commit comments

Comments
 (0)