From 070ceef5d0bb0557dd91fd795eb34457117d189b Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Fri, 5 Jul 2024 20:57:43 -0700 Subject: [PATCH 1/2] Changes to INDEX Function Fix #64 (really!), closed as stale in December 2017, another in our "better late than never" series. Excel's INDEX function doesn't really behave quite as described. If a single row is used as an argument, either in literal form `{item1, item2, item3}` or expressed as a range `A1:A6`, INDEX is happy to evaluate the array as if each entry were a row rather than a single item. PhpSpreadsheet is changed to do likewise. INDEX also returned `#REF!` when it would normally return an array (which would often be reduced to its leftmost topmost entry later). This code is deleted, invalidating one existing test, and INDEX will now operate like other functions which can return arrays. --- .../Calculation/LookupRef/Matrix.php | 15 ++++++--- .../LookupRef/IndexOnSpreadsheetTest.php | 33 +++++++++++++++++++ .../LookupRef/INDEXonSpreadsheet.php | 2 +- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php index d578854de0..228b464485 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php @@ -81,7 +81,6 @@ public static function index(mixed $matrix, mixed $rowNum = 0, mixed $columnNum } $rowNum = $rowNum ?? 0; - $originalColumnNum = $columnNum; $columnNum = $columnNum ?? 0; try { @@ -91,6 +90,17 @@ public static function index(mixed $matrix, mixed $rowNum = 0, mixed $columnNum return $e->getMessage(); } + if (is_array($matrix) && count($matrix) === 1 && $rowNum > 1) { + $matrixKey = array_keys($matrix)[0]; + if (is_array($matrix[$matrixKey])) { + $tempMatrix = []; + foreach ($matrix[$matrixKey] as $key => $value) { + $tempMatrix[$key] = [$value]; + } + $matrix = $tempMatrix; + } + } + if (!is_array($matrix) || ($rowNum > count($matrix))) { return ExcelError::REF(); } @@ -101,9 +111,6 @@ public static function index(mixed $matrix, mixed $rowNum = 0, mixed $columnNum if ($columnNum > count($columnKeys)) { return ExcelError::REF(); } - if ($originalColumnNum === null && 1 < count($columnKeys)) { - return ExcelError::REF(); - } if ($columnNum === 0) { return self::extractRowValue($matrix, $rowKeys, $rowNum); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndexOnSpreadsheetTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndexOnSpreadsheetTest.php index e893fc7f3f..3f508405ae 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndexOnSpreadsheetTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndexOnSpreadsheetTest.php @@ -34,4 +34,37 @@ public static function providerINDEXonSpreadsheet(): array { return require 'tests/data/Calculation/LookupRef/INDEXonSpreadsheet.php'; } + + /** + * @dataProvider providerIndexLiteralArrays + */ + public function testLiteralArrays(mixed $expectedResult, string $indexArgs): void + { + $sheet = $this->getSheet(); + $sheet->getCell('A10')->setValue(10); + $sheet->getCell('B10')->setValue(11); + $sheet->getCell('C10')->setValue(12); + $sheet->getCell('D10')->setValue(13); + $sheet->getCell('X10')->setValue(10); + $sheet->getCell('X11')->setValue(11); + $sheet->getCell('X12')->setValue(12); + $sheet->getCell('X13')->setValue(13); + $sheet->getCell('A1')->setValue("=INDEX($indexArgs)"); + $result = $sheet->getCell('A1')->getCalculatedValue(); + self::assertEquals($expectedResult, $result); + } + + public static function providerIndexLiteralArrays(): array + { + return [ + 'issue 64' => ['Fourth', '{"First","Second","Third","Fourth","Fifth","Sixth","Seventh"}, 4'], + 'issue 64 selecting first "row"' => ['First', '{"First","Second","Third","Fourth","Fifth","Sixth","Seventh"}, 1'], + 'array result condensed to single value' => [40, '{10,11;20,21;30,31;40,41;50,51;60,61},4'], + 'both row and column' => [41, '{10,11;20,21;30,31;40,41;50,51;60,61},4,2'], + '1*1 array' => ['first', '{"first"},1'], + 'array expressed in rows' => [20, '{10;20;30;40},2'], + 'spreadsheet single row' => [11, 'A10:D10,2'], + 'spreadsheet single column' => [13, 'X10:X13,4'], + ]; + } } diff --git a/tests/data/Calculation/LookupRef/INDEXonSpreadsheet.php b/tests/data/Calculation/LookupRef/INDEXonSpreadsheet.php index 76f6ddd464..b2202704d6 100644 --- a/tests/data/Calculation/LookupRef/INDEXonSpreadsheet.php +++ b/tests/data/Calculation/LookupRef/INDEXonSpreadsheet.php @@ -82,7 +82,7 @@ 2, ], 'Column number omitted from 2-column matrix' => [ - '#REF!', // Expected + 'abc', // Expected [ ['abc', 'def'], ['xyz', 'tuv'], From 9652ffe73a0f67c71c59c3b96e8fec20a6c86451 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 11 Jul 2024 20:38:22 -0700 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b5959339..1e518a0abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Problem rendering line chart with missing plot label. [PR #4074](https://github.com/PHPOffice/PhpSpreadsheet/pull/4074) - More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065) - Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064) +- Changes to INDEX function. [Issue #64](https://github.com/PHPOffice/PhpSpreadsheet/issues/64) [PR #4088](https://github.com/PHPOffice/PhpSpreadsheet/pull/4088) +- Ods Reader and Whitespace Text Nodes. [Issue #804](https://github.com/PHPOffice/PhpSpreadsheet/issues/804) [PR #4087](https://github.com/PHPOffice/PhpSpreadsheet/pull/4087) ## 2024-05-11 - 2.1.0