Initial work implementing the FILTER() Lookup/Reference function
Tighten up on vector formats; and provide a couple of helper methods for testing row/column vectors
This commit is contained in:
parent
1642ee4eb1
commit
f3bb61f3e4
|
|
@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
|||
|
||||
### Added
|
||||
|
||||
- Implementation of the UNIQUE() Lookup/Reference (array) function
|
||||
- Implementation of the FILTER() and UNIQUE() Lookup/Reference (array) function
|
||||
- Implementation of the ISREF() Information function.
|
||||
- Added support for reading "formatted" numeric values from Csv files; although default behaviour of reading these values as strings is preserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -1040,7 +1040,7 @@ class Calculation
|
|||
],
|
||||
'FILTER' => [
|
||||
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'functionCall' => [LookupRef\Filter::class, 'filter'],
|
||||
'argumentCount' => '2-3',
|
||||
],
|
||||
'FILTERXML' => [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class Filter
|
||||
{
|
||||
/**
|
||||
* @param mixed $lookupArray
|
||||
* @param mixed $matchArray
|
||||
* @param mixed $ifEmpty
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function filter($lookupArray, $matchArray, $ifEmpty = null)
|
||||
{
|
||||
if (!is_array($matchArray)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
$result = (Matrix::isColumnVector($matchArray))
|
||||
? self::filterByRow($lookupArray, $matchArray)
|
||||
: self::filterByColumn($lookupArray, $matchArray);
|
||||
|
||||
if (empty($result)) {
|
||||
return $ifEmpty ?? ExcelError::CALC();
|
||||
}
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
private static function filterByRow(array $lookupArray, array $matchArray): array
|
||||
{
|
||||
$matchArray = array_values(array_column($matchArray, 0));
|
||||
|
||||
return array_filter(
|
||||
array_values($lookupArray),
|
||||
function ($index) use ($matchArray): bool {
|
||||
return (bool) $matchArray[$index];
|
||||
},
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
private static function filterByColumn(array $lookupArray, array $matchArray): array
|
||||
{
|
||||
$lookupArray = Matrix::transpose($lookupArray);
|
||||
|
||||
if (count($matchArray) === 1) {
|
||||
$matchArray = array_pop($matchArray);
|
||||
}
|
||||
|
||||
array_walk(
|
||||
$matchArray,
|
||||
function (&$value): void {
|
||||
$value = [$value];
|
||||
}
|
||||
);
|
||||
|
||||
$result = self::filterByRow($lookupArray, $matchArray);
|
||||
|
||||
return Matrix::transpose($result);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,23 @@ class Matrix
|
|||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* Helper function; NOT an implementation of any Excel Function.
|
||||
*/
|
||||
public static function isColumnVector(array $values): bool
|
||||
{
|
||||
return count($values, COUNT_RECURSIVE) === (count($values, COUNT_NORMAL) * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function; NOT an implementation of any Excel Function.
|
||||
*/
|
||||
public static function isRowVector(array $values): bool
|
||||
{
|
||||
return count($values, COUNT_RECURSIVE) > 1 &&
|
||||
(count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
|
||||
}
|
||||
|
||||
/**
|
||||
* TRANSPOSE.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ class Unique
|
|||
$exactlyOnce = (bool) $exactlyOnce;
|
||||
|
||||
return ($byColumn === true)
|
||||
? self::uniqueByColumn($lookupVector, $exactlyOnce)
|
||||
: self::uniqueByRow($lookupVector, $exactlyOnce);
|
||||
? self::uniqueByColumn($lookupVector, $exactlyOnce)
|
||||
: self::uniqueByRow($lookupVector, $exactlyOnce);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Filter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FilterTest extends TestCase
|
||||
{
|
||||
public function testFilterByRow(): void
|
||||
{
|
||||
$criteria = [[true], [false], [false], [false], [true], [false], [false], [false], [false], [false], [false], [true], [false], [false], [false], [true]];
|
||||
$expectedResult = [
|
||||
['East', 'Tom', 'Apple', 6830],
|
||||
['East', 'Fritz', 'Apple', 4394],
|
||||
['South', 'Sal', 'Apple', 1310],
|
||||
['South', 'Hector', 'Apple', 98144],
|
||||
];
|
||||
$result = Filter::filter($this->sampleDataForRow(), $criteria);
|
||||
self::assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
public function testFilterByColumn(): void
|
||||
{
|
||||
$criteria = [[false, false, true, false, true, false, false, false, true, true]];
|
||||
$expectedResult = [
|
||||
['Betty', 'Charlotte', 'Oliver', 'Zoe'],
|
||||
['B', 'B', 'B', 'B'],
|
||||
[1, 2, 4, 8],
|
||||
];
|
||||
$result = Filter::filter($this->sampleDataForColumn(), $criteria);
|
||||
self::assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
public function testFilterException(): void
|
||||
{
|
||||
$criteria = 'INVALID';
|
||||
$result = Filter::filter($this->sampleDataForColumn(), $criteria);
|
||||
self::assertSame(ExcelError::VALUE(), $result);
|
||||
}
|
||||
|
||||
public function testFilterEmpty(): void
|
||||
{
|
||||
$criteria = [[false], [false], [false]];
|
||||
$expectedResult = ExcelError::CALC();
|
||||
$result = Filter::filter([[1], [2], [3]], $criteria);
|
||||
self::assertSame($expectedResult, $result);
|
||||
|
||||
$expectedResult = 'Invalid Data';
|
||||
$result = Filter::filter([[1], [2], [3]], $criteria, $expectedResult);
|
||||
self::assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
protected function sampleDataForRow(): array
|
||||
{
|
||||
return [
|
||||
['East', 'Tom', 'Apple', 6830],
|
||||
['West', 'Fred', 'Grape', 5619],
|
||||
['North', 'Amy', 'Pear', 4565],
|
||||
['South', 'Sal', 'Banana', 5323],
|
||||
['East', 'Fritz', 'Apple', 4394],
|
||||
['West', 'Sravan', 'Grape', 7195],
|
||||
['North', 'Xi', 'Pear', 5231],
|
||||
['South', 'Hector', 'Banana', 2427],
|
||||
['East', 'Tom', 'Banana', 4213],
|
||||
['West', 'Fred', 'Pear', 3239],
|
||||
['North', 'Amy', 'Grape', 6420],
|
||||
['South', 'Sal', 'Apple', 1310],
|
||||
['East', 'Fritz', 'Banana', 6274],
|
||||
['West', 'Sravan', 'Pear', 4894],
|
||||
['North', 'Xi', 'Grape', 7580],
|
||||
['South', 'Hector', 'Apple', 98144],
|
||||
];
|
||||
}
|
||||
|
||||
protected function sampleDataForColumn(): array
|
||||
{
|
||||
return [
|
||||
['Aiden', 'Andrew', 'Betty', 'Caden', 'Charlotte', 'Emma', 'Isabella', 'Mason', 'Oliver', 'Zoe'],
|
||||
['A', 'C', 'B', 'A', 'B', 'C', 'A', 'A', 'B', 'B'],
|
||||
[0, 4, 1, 2, 2, 0, 2, 4, 4, 8],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MatrixHelperFunctionsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider columnVectorProvider
|
||||
*/
|
||||
public function testIsColumnVector(bool $expectedResult, array $array): void
|
||||
{
|
||||
$result = Matrix::isColumnVector($array);
|
||||
self::assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider rowVectorProvider
|
||||
*/
|
||||
public function testIsRowVector(bool $expectedResult, array $array): void
|
||||
{
|
||||
$result = Matrix::isRowVector($array);
|
||||
self::assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
public function columnVectorProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
true,
|
||||
[
|
||||
[1], [2], [3],
|
||||
],
|
||||
],
|
||||
[
|
||||
false,
|
||||
[1, 2, 3],
|
||||
],
|
||||
[
|
||||
false,
|
||||
[
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function rowVectorProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
false,
|
||||
[
|
||||
[1], [2], [3],
|
||||
],
|
||||
],
|
||||
[
|
||||
true,
|
||||
[1, 2, 3],
|
||||
],
|
||||
[
|
||||
true,
|
||||
[[1, 2, 3]],
|
||||
],
|
||||
[
|
||||
false,
|
||||
[
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue