Skip to content

Commit bf89991

Browse files
author
MarkBaker
committed
Initial work on implementing Array-enabled for the HLOOKUP() and VLOOKUP() functions
1 parent 0ee4d96 commit bf89991

File tree

8 files changed

+107
-53
lines changed

8 files changed

+107
-53
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -765,26 +765,11 @@ parameters:
765765
count: 1
766766
path: src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
767767

768-
-
769-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\LookupBase\\:\\:checkMatch\\(\\) has parameter \\$notExactMatch with no type specified\\.$#"
770-
count: 1
771-
path: src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
772-
773-
-
774-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\LookupBase\\:\\:validateIndexLookup\\(\\) has no return type specified\\.$#"
775-
count: 1
776-
path: src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
777-
778768
-
779769
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\LookupBase\\:\\:validateIndexLookup\\(\\) has parameter \\$index_number with no type specified\\.$#"
780770
count: 1
781771
path: src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
782772

783-
-
784-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\LookupBase\\:\\:validateIndexLookup\\(\\) has parameter \\$lookup_array with no type specified\\.$#"
785-
count: 1
786-
path: src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
787-
788773
-
789774
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Matrix\\:\\:extractRowValue\\(\\) has no return type specified\\.$#"
790775
count: 1
@@ -845,31 +830,6 @@ parameters:
845830
count: 1
846831
path: src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
847832

848-
-
849-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\VLookup\\:\\:vLookupSearch\\(\\) has no return type specified\\.$#"
850-
count: 1
851-
path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
852-
853-
-
854-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\VLookup\\:\\:vLookupSearch\\(\\) has parameter \\$column with no type specified\\.$#"
855-
count: 1
856-
path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
857-
858-
-
859-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\VLookup\\:\\:vLookupSearch\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
860-
count: 1
861-
path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
862-
863-
-
864-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\VLookup\\:\\:vLookupSearch\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
865-
count: 1
866-
path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
867-
868-
-
869-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\VLookup\\:\\:vLookupSearch\\(\\) has parameter \\$notExactMatch with no type specified\\.$#"
870-
count: 1
871-
path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
872-
873833
-
874834
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\VLookup\\:\\:vlookupSort\\(\\) has no return type specified\\.$#"
875835
count: 1

src/PhpSpreadsheet/Calculation/ArrayEnabled.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,21 @@ protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int
7474

7575
return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
7676
}
77+
78+
/**
79+
* @param mixed ...$arguments
80+
*/
81+
protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, ...$arguments): array
82+
{
83+
$leadingArguments = array_slice($arguments, 0, $ignore);
84+
$ignoreArgument = array_slice($arguments, $ignore, 1);
85+
$trailingArguments = array_slice($arguments, $ignore + 1);
86+
87+
self::initialiseHelper(array_merge($leadingArguments, [[null]], $trailingArguments));
88+
$arguments = self::$arrayArgumentHelper->arguments();
89+
90+
array_splice($arguments, $ignore, 1, $ignoreArgument);
91+
92+
return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
93+
}
7794
}

src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
56
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
67
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
78
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -10,6 +11,8 @@
1011

1112
class HLookup extends LookupBase
1213
{
14+
use ArrayEnabled;
15+
1316
/**
1417
* HLOOKUP
1518
* The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
@@ -25,9 +28,12 @@ class HLookup extends LookupBase
2528
*/
2629
public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
2730
{
28-
$lookupValue = Functions::flattenSingleValue($lookupValue);
31+
if (is_array($lookupValue)) {
32+
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
33+
}
34+
2935
$indexNumber = Functions::flattenSingleValue($indexNumber);
30-
$notExactMatch = ($notExactMatch === null) ? true : Functions::flattenSingleValue($notExactMatch);
36+
$notExactMatch = ($notExactMatch === null) ? true : (bool) Functions::flattenSingleValue($notExactMatch);
3137
$lookupArray = self::convertLiteralArray($lookupArray);
3238

3339
try {
@@ -44,7 +50,7 @@ public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExac
4450

4551
$firstkey = $f[0] - 1;
4652
$returnColumn = $firstkey + $indexNumber;
47-
$firstColumn = array_shift($f);
53+
$firstColumn = array_shift($f) ?? 1;
4854
$rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
4955

5056
if ($rowNumber !== null) {
@@ -57,10 +63,9 @@ public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExac
5763

5864
/**
5965
* @param mixed $lookupValue The value that you want to match in lookup_array
60-
* @param mixed $column The column to look up
61-
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
66+
* @param int|string $column
6267
*/
63-
private static function hLookupSearch($lookupValue, array $lookupArray, $column, $notExactMatch): ?int
68+
private static function hLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
6469
{
6570
$lookupLower = StringHelper::strToLower($lookupValue);
6671

src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
abstract class LookupBase
99
{
10-
protected static function validateIndexLookup($lookup_array, $index_number)
10+
protected static function validateIndexLookup(array $lookup_array, $index_number): int
1111
{
1212
// index_number must be a number greater than or equal to 1
1313
if (!is_numeric($index_number) || $index_number < 1) {
@@ -25,7 +25,7 @@ protected static function validateIndexLookup($lookup_array, $index_number)
2525
protected static function checkMatch(
2626
bool $bothNumeric,
2727
bool $bothNotNumeric,
28-
$notExactMatch,
28+
bool $notExactMatch,
2929
int $rowKey,
3030
string $cellDataLower,
3131
string $lookupLower,

src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
56
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
67
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
78
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
89
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
910

1011
class VLookup extends LookupBase
1112
{
13+
use ArrayEnabled;
14+
1215
/**
1316
* VLOOKUP
1417
* The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
@@ -24,9 +27,12 @@ class VLookup extends LookupBase
2427
*/
2528
public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
2629
{
27-
$lookupValue = Functions::flattenSingleValue($lookupValue);
30+
if (is_array($lookupValue)) {
31+
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
32+
}
33+
2834
$indexNumber = Functions::flattenSingleValue($indexNumber);
29-
$notExactMatch = ($notExactMatch === null) ? true : Functions::flattenSingleValue($notExactMatch);
35+
$notExactMatch = ($notExactMatch === null) ? true : (bool) Functions::flattenSingleValue($notExactMatch);
3036

3137
try {
3238
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
@@ -41,7 +47,7 @@ public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExac
4147
}
4248
$columnKeys = array_keys($lookupArray[$firstRow]);
4349
$returnColumn = $columnKeys[--$indexNumber];
44-
$firstColumn = array_shift($columnKeys);
50+
$firstColumn = array_shift($columnKeys) ?? 1;
4551

4652
if (!$notExactMatch) {
4753
uasort($lookupArray, ['self', 'vlookupSort']);
@@ -71,7 +77,11 @@ private static function vlookupSort($a, $b)
7177
return ($aLower < $bLower) ? -1 : 1;
7278
}
7379

74-
private static function vLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
80+
/**
81+
* @param mixed $lookupValue The value that you want to match in lookup_array
82+
* @param int|string $column
83+
*/
84+
private static function vLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
7585
{
7686
$lookupLower = StringHelper::strToLower($lookupValue);
7787

tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/HLookupTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
56
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
67
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
78
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -89,4 +90,40 @@ public function testGrandfathered(): void
8990
);
9091
self::assertSame($expectedResult, $result);
9192
}
93+
94+
/**
95+
* @dataProvider providerHLookupArray
96+
*/
97+
public function testHLookupArray(array $expectedResult, string $values, string $database, string $index): void
98+
{
99+
$calculation = Calculation::getInstance();
100+
101+
$formula = "=HLOOKUP({$values}, {$database}, {$index}, false)";
102+
$result = $calculation->_calculateFormulaValue($formula);
103+
self::assertEquals($expectedResult, $result);
104+
}
105+
106+
public function providerHLookupArray(): array
107+
{
108+
return [
109+
'row vector #1' => [
110+
[[4, 9]],
111+
'{"Axles", "Bolts"}',
112+
'{"Axles", "Bearings", "Bolts"; 4, 4, 9; 5, 7, 10; 6, 8, 11}',
113+
'2',
114+
],
115+
'row vector #2' => [
116+
[[5, 7]],
117+
'{"Axles", "Bearings"}',
118+
'{"Axles", "Bearings", "Bolts"; 4, 4, 9; 5, 7, 10; 6, 8, 11}',
119+
'3',
120+
],
121+
'row/column vectors' => [
122+
[[4, 9], [5, 10]],
123+
'{"Axles", "Bolts"}',
124+
'{"Axles", "Bearings", "Bolts"; 4, 4, 9; 5, 7, 10; 6, 8, 11}',
125+
'{2; 3}',
126+
],
127+
];
128+
}
92129
}

tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/VLookupTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
56
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
67
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
78
use PHPUnit\Framework\TestCase;
@@ -28,4 +29,28 @@ public function providerVLOOKUP(): array
2829
{
2930
return require 'tests/data/Calculation/LookupRef/VLOOKUP.php';
3031
}
32+
33+
/**
34+
* @dataProvider providerVLookupArray
35+
*/
36+
public function testVLookupArray(array $expectedResult, string $values, string $database, string $index): void
37+
{
38+
$calculation = Calculation::getInstance();
39+
40+
$formula = "=VLOOKUP({$values}, {$database}, {$index}, false)";
41+
$result = $calculation->_calculateFormulaValue($formula);
42+
self::assertEquals($expectedResult, $result);
43+
}
44+
45+
public function providerVLookupArray(): array
46+
{
47+
return [
48+
'row vector' => [
49+
[[4.19, 5.77, 4.14]],
50+
'{"Orange", "Green", "Red"}',
51+
'{"Red", 4.14; "Orange", 4.19; "Yellow", 5.17; "Green", 5.77; "Blue", 6.39}',
52+
'2',
53+
],
54+
];
55+
}
3156
}

tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/AbsTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class AbsTest extends AllSetupTeardown
1212
* @param mixed $expectedResult
1313
* @param mixed $number
1414
*/
15-
public function testRound($expectedResult, $number = 'omitted'): void
15+
public function testAbs($expectedResult, $number = 'omitted'): void
1616
{
1717
$sheet = $this->getSheet();
1818
$this->mightHaveException($expectedResult);

0 commit comments

Comments
 (0)