Lookup ref further tests and examples (#1918)
* Extract LookupRef\INDEX() into index() method of LookupRef\Matrix class Additional tests * Bugfix for returning a column using INDEX() * Some improvements to ROW() and COLUMN() * Simplify some of the INDEX() logic, eliminating redundant code
This commit is contained in:
parent
499ce61cf7
commit
2259de578b
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../../Header.php';
|
||||
|
||||
$helper->log('Returns a text reference to a single cell in a worksheet.');
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$worksheet->getCell('A1')->setValue('=ADDRESS(2,3)');
|
||||
$worksheet->getCell('A2')->setValue('=ADDRESS(2,3,2)');
|
||||
$worksheet->getCell('A3')->setValue('=ADDRESS(2,3,2,FALSE)');
|
||||
$worksheet->getCell('A4')->setValue('=ADDRESS(2,3,1,FALSE,"[Book1]Sheet1")');
|
||||
$worksheet->getCell('A5')->setValue('=ADDRESS(2,3,1,FALSE,"EXCEL SHEET")');
|
||||
|
||||
for ($row = 1; $row <= 5; ++$row) {
|
||||
$cell = $worksheet->getCell("A{$row}");
|
||||
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../../Header.php';
|
||||
|
||||
$helper->log('Returns the column index of a cell.');
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$worksheet->getCell('A1')->setValue('=COLUMN(C13)');
|
||||
$worksheet->getCell('A2')->setValue('=COLUMN(E13:G15)');
|
||||
$worksheet->getCell('F1')->setValue('=COLUMN()');
|
||||
|
||||
for ($row = 1; $row <= 2; ++$row) {
|
||||
$cell = $worksheet->getCell("A{$row}");
|
||||
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
}
|
||||
|
||||
$cell = $worksheet->getCell('F1');
|
||||
$helper->log("F1: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../../Header.php';
|
||||
|
||||
$helper->log('Returns the number of columns in an array or reference.');
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$worksheet->getCell('A1')->setValue('=COLUMNS(C1:G4)');
|
||||
$worksheet->getCell('A2')->setValue('=COLUMNS({1,2,3;4,5,6})');
|
||||
$worksheet->getCell('A3')->setValue('=ROWS(C1:E4 D3:G5)');
|
||||
$worksheet->getCell('A4')->setValue('=COLUMNS(1:1)');
|
||||
|
||||
for ($row = 1; $row <= 4; ++$row) {
|
||||
$cell = $worksheet->getCell("A{$row}");
|
||||
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../../Header.php';
|
||||
|
||||
$helper->log('Returns the row index of a cell.');
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$data1 = [
|
||||
['Apples', 'Lemons'],
|
||||
['Bananas', 'Pears'],
|
||||
];
|
||||
|
||||
$data2 = [
|
||||
[4, 6],
|
||||
[5, 3],
|
||||
[6, 9],
|
||||
[7, 5],
|
||||
[8, 3],
|
||||
];
|
||||
|
||||
$worksheet->fromArray($data1, null, 'A1');
|
||||
$worksheet->fromArray($data2, null, 'C1');
|
||||
|
||||
$worksheet->getCell('A11')->setValue('=INDEX(A1:B2, 2, 2)');
|
||||
$worksheet->getCell('A12')->setValue('=INDEX(A1:B2, 2, 1)');
|
||||
$worksheet->getCell('A13')->setValue('=INDEX({1,2;3,4}, 0, 2)');
|
||||
$worksheet->getCell('A14')->setValue('=INDEX(C1:C5, 5)');
|
||||
$worksheet->getCell('A15')->setValue('=INDEX(C1:D5, 5, 2)');
|
||||
$worksheet->getCell('A16')->setValue('=SUM(INDEX(C1:D5, 5, 0))');
|
||||
|
||||
for ($row = 11; $row <= 16; ++$row) {
|
||||
$cell = $worksheet->getCell("A{$row}");
|
||||
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../../Header.php';
|
||||
|
||||
$helper->log('Returns the row index of a cell.');
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$worksheet->getCell('A1')->setValue('=ROW(C13)');
|
||||
$worksheet->getCell('A2')->setValue('=ROW(E19:G21)');
|
||||
$worksheet->getCell('A3')->setValue('=ROW()');
|
||||
|
||||
for ($row = 1; $row <= 3; ++$row) {
|
||||
$cell = $worksheet->getCell("A{$row}");
|
||||
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../../Header.php';
|
||||
|
||||
$helper->log('Returns the row index of a cell.');
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$worksheet->getCell('A1')->setValue('=ROWS(C1:E4)');
|
||||
$worksheet->getCell('A2')->setValue('=ROWS({1,2,3;4,5,6})');
|
||||
$worksheet->getCell('A3')->setValue('=ROWS(C1:E4 D3:G5)');
|
||||
|
||||
for ($row = 1; $row <= 3; ++$row) {
|
||||
$cell = $worksheet->getCell("A{$row}");
|
||||
$helper->log("A{$row}: {$cell->getValue()} => {$cell->getCalculatedValue()}");
|
||||
}
|
||||
|
|
@ -529,59 +529,21 @@ class LookupRef
|
|||
* Excel Function:
|
||||
* =INDEX(range_array, row_num, [column_num])
|
||||
*
|
||||
* @param mixed $arrayValues A range of cells or an array constant
|
||||
* @param mixed $rowNum The row in array from which to return a value. If row_num is omitted, column_num is required.
|
||||
* @param mixed $columnNum The column in array from which to return a value. If column_num is omitted, row_num is required.
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the index() method in the LookupRef\Matrix class instead
|
||||
*
|
||||
* @param mixed $rowNum The row in the array or range from which to return a value.
|
||||
* If row_num is omitted, column_num is required.
|
||||
* @param mixed $columnNum The column in the array or range from which to return a value.
|
||||
* If column_num is omitted, row_num is required.
|
||||
* @param mixed $matrix
|
||||
*
|
||||
* @return mixed the value of a specified cell or array of cells
|
||||
*/
|
||||
public static function INDEX($arrayValues, $rowNum = 0, $columnNum = 0)
|
||||
public static function INDEX($matrix, $rowNum = 0, $columnNum = 0)
|
||||
{
|
||||
$rowNum = Functions::flattenSingleValue($rowNum);
|
||||
$columnNum = Functions::flattenSingleValue($columnNum);
|
||||
|
||||
if (($rowNum < 0) || ($columnNum < 0)) {
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (!is_array($arrayValues) || ($rowNum > count($arrayValues))) {
|
||||
return Functions::REF();
|
||||
}
|
||||
|
||||
$rowKeys = array_keys($arrayValues);
|
||||
$columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
|
||||
|
||||
if ($columnNum > count($columnKeys)) {
|
||||
return Functions::VALUE();
|
||||
} elseif ($columnNum == 0) {
|
||||
if ($rowNum == 0) {
|
||||
return $arrayValues;
|
||||
}
|
||||
$rowNum = $rowKeys[--$rowNum];
|
||||
$returnArray = [];
|
||||
foreach ($arrayValues as $arrayColumn) {
|
||||
if (is_array($arrayColumn)) {
|
||||
if (isset($arrayColumn[$rowNum])) {
|
||||
$returnArray[] = $arrayColumn[$rowNum];
|
||||
} else {
|
||||
return [$rowNum => $arrayValues[$rowNum]];
|
||||
}
|
||||
} else {
|
||||
return $arrayValues[$rowNum];
|
||||
}
|
||||
}
|
||||
|
||||
return $returnArray;
|
||||
}
|
||||
$columnNum = $columnKeys[--$columnNum];
|
||||
if ($rowNum > count($rowKeys)) {
|
||||
return Functions::VALUE();
|
||||
} elseif ($rowNum == 0) {
|
||||
return $arrayValues[$columnNum];
|
||||
}
|
||||
$rowNum = $rowKeys[--$rowNum];
|
||||
|
||||
return $arrayValues[$rowNum][$columnNum];
|
||||
return Matrix::index($matrix, $rowNum, $columnNum);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Matrix
|
||||
{
|
||||
/**
|
||||
|
|
@ -30,4 +32,73 @@ class Matrix
|
|||
|
||||
return $returnMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* INDEX.
|
||||
*
|
||||
* Uses an index to choose a value from a reference or array
|
||||
*
|
||||
* Excel Function:
|
||||
* =INDEX(range_array, row_num, [column_num])
|
||||
*
|
||||
* @param mixed $matrix A range of cells or an array constant
|
||||
* @param mixed $rowNum The row in the array or range from which to return a value.
|
||||
* If row_num is omitted, column_num is required.
|
||||
* @param mixed $columnNum The column in the array or range from which to return a value.
|
||||
* If column_num is omitted, row_num is required.
|
||||
*
|
||||
* @return mixed the value of a specified cell or array of cells
|
||||
*/
|
||||
public static function index($matrix, $rowNum = 0, $columnNum = 0)
|
||||
{
|
||||
$rowNum = Functions::flattenSingleValue($rowNum);
|
||||
$columnNum = Functions::flattenSingleValue($columnNum);
|
||||
|
||||
if (!is_numeric($rowNum) || !is_numeric($columnNum) || ($rowNum < 0) || ($columnNum < 0)) {
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (!is_array($matrix) || ($rowNum > count($matrix))) {
|
||||
return Functions::REF();
|
||||
}
|
||||
|
||||
$rowKeys = array_keys($matrix);
|
||||
$columnKeys = @array_keys($matrix[$rowKeys[0]]);
|
||||
|
||||
if ($columnNum > count($columnKeys)) {
|
||||
return Functions::REF();
|
||||
}
|
||||
|
||||
if ($columnNum == 0) {
|
||||
return self::extractRowValue($matrix, $rowKeys, $rowNum);
|
||||
}
|
||||
|
||||
$columnNum = $columnKeys[--$columnNum];
|
||||
if ($rowNum == 0) {
|
||||
return array_map(
|
||||
function ($value) {
|
||||
return [$value];
|
||||
},
|
||||
array_column($matrix, $columnNum)
|
||||
);
|
||||
}
|
||||
$rowNum = $rowKeys[--$rowNum];
|
||||
|
||||
return $matrix[$rowNum][$columnNum];
|
||||
}
|
||||
|
||||
private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum)
|
||||
{
|
||||
if ($rowNum == 0) {
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
$rowNum = $rowKeys[--$rowNum];
|
||||
$row = $matrix[$rowNum];
|
||||
if (is_array($row)) {
|
||||
return [$rowNum => $row];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,24 +39,24 @@ class RowColumnInformation
|
|||
|
||||
return (int) Coordinate::columnIndexFromString($columnKey);
|
||||
}
|
||||
} else {
|
||||
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
}
|
||||
|
||||
[, $cellAddress] = Worksheet::extractSheetTitle((string) $cellAddress, true);
|
||||
if (strpos($cellAddress, ':') !== false) {
|
||||
[$startAddress, $endAddress] = explode(':', $cellAddress);
|
||||
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
|
||||
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
|
||||
$returnValue = [];
|
||||
do {
|
||||
$returnValue[] = (int) Coordinate::columnIndexFromString($startAddress);
|
||||
} while ($startAddress++ != $endAddress);
|
||||
|
||||
return $returnValue;
|
||||
return range(
|
||||
(int) Coordinate::columnIndexFromString($startAddress),
|
||||
(int) Coordinate::columnIndexFromString($endAddress)
|
||||
);
|
||||
}
|
||||
|
||||
$cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
|
||||
|
||||
return (int) Coordinate::columnIndexFromString($cellAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* COLUMNS.
|
||||
|
|
@ -73,7 +73,7 @@ class RowColumnInformation
|
|||
*/
|
||||
public static function COLUMNS($cellAddress = null)
|
||||
{
|
||||
if ($cellAddress === null || $cellAddress === '') {
|
||||
if ($cellAddress === null || (is_string($cellAddress) && trim($cellAddress) === '')) {
|
||||
return 1;
|
||||
} elseif (!is_array($cellAddress)) {
|
||||
return Functions::VALUE();
|
||||
|
|
@ -114,29 +114,30 @@ class RowColumnInformation
|
|||
}
|
||||
|
||||
if (is_array($cellAddress)) {
|
||||
foreach ($cellAddress as $columnKey => $rowValue) {
|
||||
foreach ($rowValue as $rowKey => $cellValue) {
|
||||
foreach ($cellAddress as $rowKey => $rowValue) {
|
||||
foreach ($rowValue as $columnKey => $cellValue) {
|
||||
return (int) preg_replace('/\D/', '', $rowKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
}
|
||||
|
||||
[, $cellAddress] = Worksheet::extractSheetTitle((string) $cellAddress, true);
|
||||
if (strpos($cellAddress, ':') !== false) {
|
||||
[$startAddress, $endAddress] = explode(':', $cellAddress);
|
||||
$startAddress = preg_replace('/\D/', '', $startAddress);
|
||||
$endAddress = preg_replace('/\D/', '', $endAddress);
|
||||
$returnValue = [];
|
||||
do {
|
||||
$returnValue[][] = (int) $startAddress;
|
||||
} while ($startAddress++ != $endAddress);
|
||||
|
||||
return $returnValue;
|
||||
return array_map(
|
||||
function ($value) {
|
||||
return [$value];
|
||||
},
|
||||
range($startAddress, $endAddress)
|
||||
);
|
||||
}
|
||||
[$cellAddress] = explode(':', $cellAddress);
|
||||
|
||||
return (int) preg_replace('/\D/', '', $cellAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ROWS.
|
||||
|
|
@ -153,7 +154,7 @@ class RowColumnInformation
|
|||
*/
|
||||
public static function ROWS($cellAddress = null)
|
||||
{
|
||||
if ($cellAddress === null || $cellAddress === '') {
|
||||
if ($cellAddress === null || (is_string($cellAddress) && trim($cellAddress) === '')) {
|
||||
return 1;
|
||||
} elseif (!is_array($cellAddress)) {
|
||||
return Functions::VALUE();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class IndexTest extends TestCase
|
|||
public function testINDEX($expectedResult, ...$args): void
|
||||
{
|
||||
$result = LookupRef::INDEX(...$args);
|
||||
// var_dump($result);
|
||||
self::assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ return [
|
|||
-1,
|
||||
],
|
||||
[
|
||||
'#VALUE!', // Expected
|
||||
'#REF!', // Expected
|
||||
// Input
|
||||
[
|
||||
'20' => ['R' => 1, 'S' => 3],
|
||||
|
|
@ -63,6 +63,16 @@ return [
|
|||
2,
|
||||
10,
|
||||
],
|
||||
[
|
||||
'#REF!', // Expected
|
||||
// Input
|
||||
[
|
||||
'20' => ['R' => 1, 'S' => 3],
|
||||
'21' => ['R' => 2, 'S' => 4],
|
||||
],
|
||||
10,
|
||||
2,
|
||||
],
|
||||
[
|
||||
4, // Expected
|
||||
// Input
|
||||
|
|
@ -87,4 +97,64 @@ return [
|
|||
'21' => ['R' => 2],
|
||||
],
|
||||
],
|
||||
[
|
||||
'Pears',
|
||||
[
|
||||
['Apples', 'Lemons'],
|
||||
['Bananas', 'Pears'],
|
||||
],
|
||||
2,
|
||||
2,
|
||||
],
|
||||
[
|
||||
'Bananas',
|
||||
[
|
||||
['Apples', 'Lemons'],
|
||||
['Bananas', 'Pears'],
|
||||
],
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
3,
|
||||
[
|
||||
[4, 6],
|
||||
[5, 3],
|
||||
[6, 9],
|
||||
[7, 5],
|
||||
[8, 3],
|
||||
],
|
||||
5,
|
||||
2,
|
||||
],
|
||||
[
|
||||
[4 => [8, 3]],
|
||||
[
|
||||
[4, 6],
|
||||
[5, 3],
|
||||
[6, 9],
|
||||
[7, 5],
|
||||
[8, 3],
|
||||
],
|
||||
5,
|
||||
0,
|
||||
],
|
||||
[
|
||||
[
|
||||
[6],
|
||||
[3],
|
||||
[9],
|
||||
[5],
|
||||
[3],
|
||||
],
|
||||
[
|
||||
[4, 6],
|
||||
[5, 3],
|
||||
[6, 9],
|
||||
[7, 5],
|
||||
[8, 3],
|
||||
],
|
||||
0,
|
||||
2,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ return [
|
|||
[[10], [11], [12]],
|
||||
'C10:D12',
|
||||
],
|
||||
[
|
||||
4,
|
||||
[
|
||||
4 => ['B' => 'B5', 'C' => 'C5', 'D' => 'D5'],
|
||||
5 => ['B' => 'B5', 'C' => 'C5', 'D' => 'D5'],
|
||||
],
|
||||
],
|
||||
[
|
||||
[[10], [11], [12]],
|
||||
'Sheet1!C10:C12',
|
||||
|
|
|
|||
Loading…
Reference in New Issue