Skip to content

Commit 08ba00b

Browse files
committed
Populate Rest of Array Cells, UNIQUE Changes
See issue #4062. When calculating an array formula, populate all the cells associated with the result. This is almost the same as Excel's behavior. As yet, there is no attempt to create a #SPILL error, so cells may be inappropriately overwritten. Also, if the array size shrinks (e.g. there are fewer unique values than before), no attempt is made to unpopulate the cells which were in range but are now outside the new dimensions. Spill and unpopulation are somewhat related, and will probably be handled at the same time, but their time has not yet come. UNIQUE, at least for rows, was treating all cell (calculated) values as strings. This is not the same behavior as Excel, which will preserve datatypes, and treat int 3 and string 3 as unique values. Excel will, however, treat int 3 and float 3.0 as non-unique. Within UNIQUE, private function uniqueByRow is changed to try to preserve the the datatype when executing (it will probably treat 3.0 as int - I don't know how I can, or even if I should attempt to, do better - but no int nor float should be treated as a string).
1 parent b79cd20 commit 08ba00b

File tree

6 files changed

+411
-21
lines changed

6 files changed

+411
-21
lines changed

src/PhpSpreadsheet/Calculation/LookupRef/Unique.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,17 @@ private static function uniqueByRow(array $lookupVector, bool $exactlyOnce): mix
4040
array_walk(
4141
$lookupVector,
4242
function (array &$value): void {
43-
$value = implode(chr(0x00), $value);
43+
$valuex = '';
44+
$separator = '';
45+
$numericIndicator = "\x01";
46+
foreach ($value as $cellValue) {
47+
$valuex .= $separator . $cellValue;
48+
$separator = "\x00";
49+
if (is_int($cellValue) || is_float($cellValue)) {
50+
$valuex .= $numericIndicator;
51+
}
52+
}
53+
$value = $valuex;
4454
}
4555
);
4656

@@ -60,7 +70,14 @@ function (array &$value): void {
6070
array_walk(
6171
$result,
6272
function (string &$value): void {
63-
$value = explode(chr(0x00), $value);
73+
$value = explode("\x00", $value);
74+
foreach ($value as &$stringValue) {
75+
if (str_ends_with($stringValue, "\x01")) {
76+
// x01 should only end a string which is otherwise a float or int,
77+
// so phpstan is technically correct but what it fears should not happen.
78+
$stringValue = 0 + substr($stringValue, 0, -1); //@phpstan-ignore-line
79+
}
80+
}
6481
}
6582
);
6683

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,22 @@ public function getCalculatedValue(bool $resetLog = true): mixed
390390
}
391391
}
392392
}
393+
if (is_array($result)) {
394+
$newRow = $row = $this->getRow();
395+
$column = $this->getColumn();
396+
foreach ($result as $resultRow) {
397+
$newColumn = $column;
398+
$resultRowx = is_array($resultRow) ? $resultRow : [$resultRow];
399+
foreach ($resultRowx as $resultValue) {
400+
if ($row !== $newRow || $column !== $newColumn) {
401+
$this->getWorksheet()->getCell($newColumn . $newRow)->setValue($resultValue);
402+
}
403+
++$newColumn;
404+
}
405+
++$newRow;
406+
}
407+
$this->getWorksheet()->getCell($column . $row);
408+
}
393409
} catch (SpreadsheetException $ex) {
394410
if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
395411
return $this->calculatedValue; // Fallback for calculations referencing external files.

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class UniqueTest extends TestCase
1616
public function testUnique(array $expectedResult, array $lookupRef, bool $byColumn = false, bool $exactlyOnce = false): void
1717
{
1818
$result = LookupRef\Unique::unique($lookupRef, $byColumn, $exactlyOnce);
19-
self::assertEquals($expectedResult, $result);
19+
self::assertSame($expectedResult, $result);
2020
}
2121

2222
public function testUniqueException(): void
@@ -36,10 +36,10 @@ public function testUniqueException(): void
3636
];
3737

3838
$result = LookupRef\Unique::unique($rowLookupData, false, true);
39-
self::assertEquals(ExcelError::CALC(), $result);
39+
self::assertSame(ExcelError::CALC(), $result);
4040

4141
$result = LookupRef\Unique::unique($columnLookupData, true, true);
42-
self::assertEquals(ExcelError::CALC(), $result);
42+
self::assertSame(ExcelError::CALC(), $result);
4343
}
4444

4545
public function testUniqueWithScalar(): void
@@ -145,13 +145,16 @@ public static function uniqueTestProvider(): array
145145
],
146146
],
147147
[
148-
[[1.2], [2.1], [2.2], [3.0]],
148+
[[1.2], [2.1], [2.2], [3], ['3'], [8.7]],
149149
[
150150
[1.2],
151151
[1.2],
152152
[2.1],
153153
[2.2],
154154
[3.0],
155+
[3],
156+
['3'],
157+
[8.7],
155158
],
156159
],
157160
];

0 commit comments

Comments
 (0)