Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrections for HLOOKUP Function #2330

Merged
merged 2 commits into from
Oct 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 0 additions & 30 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -760,36 +760,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php

-
message: "#^Binary operation \"\\-\" between int\\|string and 1 results in an error\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\HLookup\\:\\:hLookupSearch\\(\\) has no return typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\HLookup\\:\\:hLookupSearch\\(\\) has parameter \\$column with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\HLookup\\:\\:hLookupSearch\\(\\) has parameter \\$lookupArray with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\HLookup\\:\\:hLookupSearch\\(\\) has parameter \\$lookupValue with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\HLookup\\:\\:hLookupSearch\\(\\) has parameter \\$notExactMatch with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Lookup\\:\\:verifyResultVector\\(\\) has no return typehint specified\\.$#"
count: 1
Expand Down
36 changes: 33 additions & 3 deletions src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;

class HLookup extends LookupBase
Expand All @@ -26,6 +27,7 @@ public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExac
$lookupValue = Functions::flattenSingleValue($lookupValue);
$indexNumber = Functions::flattenSingleValue($indexNumber);
$notExactMatch = ($notExactMatch === null) ? true : Functions::flattenSingleValue($notExactMatch);
$lookupArray = self::convertLiteralArray($lookupArray);

try {
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
Expand All @@ -46,13 +48,18 @@ public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExac

if ($rowNumber !== null) {
// otherwise return the appropriate value
return $lookupArray[$returnColumn][$rowNumber];
return $lookupArray[$returnColumn][Coordinate::stringFromColumnIndex($rowNumber)];
}

return Functions::NA();
}

private static function hLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
/**
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $column The column to look up
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
*/
private static function hLookupSearch($lookupValue, array $lookupArray, $column, $notExactMatch): ?int
{
$lookupLower = StringHelper::strToLower($lookupValue);

Expand All @@ -74,7 +81,7 @@ private static function hLookupSearch($lookupValue, $lookupArray, $column, $notE
$bothNumeric,
$bothNotNumeric,
$notExactMatch,
$rowKey,
Coordinate::columnIndexFromString($rowKey),
$cellDataLower,
$lookupLower,
$rowNumber
Expand All @@ -83,4 +90,27 @@ private static function hLookupSearch($lookupValue, $lookupArray, $column, $notE

return $rowNumber;
}

private static function convertLiteralArray(array $lookupArray): array
{
if (array_key_exists(0, $lookupArray)) {
$lookupArray2 = [];
$row = 0;
foreach ($lookupArray as $arrayVal) {
++$row;
if (!is_array($arrayVal)) {
$arrayVal = [$arrayVal];
}
$arrayVal2 = [];
foreach ($arrayVal as $key2 => $val2) {
$index = Coordinate::stringFromColumnIndex($key2 + 1);
$arrayVal2[$index] = $val2;
}
$lookupArray2[$row] = $arrayVal2;
}
$lookupArray = $lookupArray2;
}

return $lookupArray;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,91 @@

namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;

use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;

class HLookupTest extends TestCase
{
protected function setUp(): void
{
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
}

/**
* @dataProvider providerHLOOKUP
*
* @param mixed $expectedResult
* @param mixed $lookup
* @param mixed $rowIndex
*/
public function testHLOOKUP($expectedResult, ...$args): void
public function testHLOOKUP($expectedResult, $lookup, array $values, $rowIndex, ?bool $rangeLookup = null): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$maxRow = 0;
$maxCol = 0;
$maxColLetter = 'A';
$row = 0;
foreach ($values as $rowValues) {
++$row;
++$maxRow;
$col = 0;
if (!is_array($rowValues)) {
$rowValues = [$rowValues];
}
foreach ($rowValues as $cellValue) {
++$col;
$colLetter = Coordinate::stringFromColumnIndex($col);
if ($col > $maxCol) {
$maxCol = $col;
$maxColLetter = $colLetter;
}
if ($cellValue !== null) {
$sheet->getCell("$colLetter$row")->setValue($cellValue);
}
}
}

$boolArg = self::parseRangeLookup($rangeLookup);
$sheet->getCell('ZZ8')->setValue($lookup);
$sheet->getCell('ZZ7')->setValue($rowIndex);
$sheet->getCell('ZZ1')->setValue("=HLOOKUP(ZZ8, A1:$maxColLetter$maxRow, ZZ7$boolArg)");
self::assertEquals($expectedResult, $sheet->getCell('ZZ1')->getCalculatedValue());

$spreadsheet->disconnectWorksheets();
}

private static function parseRangeLookup(?bool $rangeLookup): string
{
$result = LookupRef::HLOOKUP(...$args);
self::assertEquals($expectedResult, $result);
if ($rangeLookup === null) {
return '';
}

return $rangeLookup ? ', true' : ', false';
}

public function providerHLOOKUP(): array
{
return require 'tests/data/Calculation/LookupRef/HLOOKUP.php';
}

public function testGrandfathered(): void
{
// Second parameter is supposed to be array of arrays.
// Some old tests called function directly using array of strings;
// ensure these work as before.
$expectedResult = '#REF!';
$result = LookupRef::HLOOKUP(
'Selection column',
['Selection column', 'Value to retrieve'],
5,
false
);
self::assertSame($expectedResult, $result);
$expectedResult = 'Value to retrieve';
$result = LookupRef::HLOOKUP(
'Selection column',
['Selection column', 'Value to retrieve'],
2,
false
);
self::assertSame($expectedResult, $result);
}
}
22 changes: 21 additions & 1 deletion tests/data/Calculation/LookupRef/HLOOKUP.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function partsGrid(): array
false,
],
[
'#REF!',
'#N/A',
'B',
[
'Selection column',
Expand All @@ -142,6 +142,26 @@ function partsGrid(): array
2,
false,
],
[
'#REF!',
'Selection column',
[
'Selection column',
'Value to retrieve',
],
5,
false,
],
[
'Selection column',
'Selection column',
[
'Selection column',
'Value to retrieve',
],
1,
false,
],
[
0.61,
'Ed',
Expand Down