Skip to content

Commit c84e334

Browse files
author
MarkBaker
committed
Update documentation with details of array formula handling, and the Spill Operator
1 parent abf4f9c commit c84e334

10 files changed

+150
-3
lines changed

docs/topics/calculation-engine.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ formula calculation capabilities. A cell can be of a value type
1010
which can be evaluated). For example, the formula `=SUM(A1:A10)`
1111
evaluates to the sum of values in A1, A2, ..., A10.
1212

13+
Calling `getValue()` on a cell that contains a formula will return the formula itself.
14+
1315
To calculate a formula, you can call the cell containing the formula’s
1416
method `getCalculatedValue()`, for example:
1517

@@ -22,6 +24,28 @@ with PhpSpreadsheet, it evaluates to the value "64":
2224

2325
![09-command-line-calculation.png](./images/09-command-line-calculation.png)
2426

27+
Calling `getCalculatedValue()` on a cell that doesn't contain a formula will simply return the value of that cell; but if the cell does contain a formula, then PhpSpreadsheet will evaluate that formula to calculate the result.
28+
29+
There are a few useful mehods to help identify whether a cell contains a formula or a simple value; and if a formula, to provide further information about it:
30+
31+
```php
32+
$spreadsheet->getActiveSheet()->getCell('E11')->isFormula();
33+
```
34+
will return a boolean true/false, telling you whether that cell contains a formula or not, so you can determine if a call to `getCalculatedVaue()` will need to perform an evaluation.
35+
36+
A formula can be either a simple formula, or an array formula; and another method will identify which it is:
37+
```php
38+
$spreadsheet->getActiveSheet()->getCell('E11')->isArrayFormula();
39+
```
40+
Finally, an array formula might result in a single cell result, or a result that can spill over into a range of cells; so for array formulae the following method also exists:
41+
```php
42+
$spreadsheet->getActiveSheet()->getCell('E11')->arrayFormulaRange();
43+
```
44+
which returns a string containing a cell reference (e.g. `E11`) or a cell range reference (e.g. `E11:G13`).
45+
46+
47+
### Adjustments to formulae when Inserting/Deleting Columns/Rows
48+
2549
Another nice feature of PhpSpreadsheet's formula parser, is that it can
2650
automatically adjust a formula when inserting/removing rows/columns.
2751
Here's an example:
@@ -149,8 +173,7 @@ number of seconds from the PHP/Unix base date. The PHP/Unix base date
149173
(0) is 00:00 UST on 1st January 1970. This value can be positive or
150174
negative: so a value of -3600 would be 23:00 hrs on 31st December 1969;
151175
while a value of +3600 would be 01:00 hrs on 1st January 1970. This
152-
gives PHP a date range of between 14th December 1901 and 19th January
153-
2038.
176+
gives 32-bit PHP a date range of between 14th December 1901 and 19th January 2038.
154177

155178
#### PHP `DateTime` Objects
156179

Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading

docs/topics/recipes.md

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,128 @@ is further explained in [the calculation engine](./calculation-engine.md).
165165
$value = $spreadsheet->getActiveSheet()->getCell('B8')->getCalculatedValue();
166166
```
167167

168+
### Array Formulae
169+
170+
With version 2.0.0 of PhpSpreadsheet, we've introduced support for Excel "array formulae".
171+
172+
As a basic example, let's look at a receipt for buying some fruit:
173+
174+
![12-CalculationEngine-Basic-Formula.png](./images/12-CalculationEngine-Basic-Formula.png)
175+
176+
We can provide a "Cost" formula for each row of the receipt by multiplying the "Quantity" (column `B`) by the "Price" (column `C`); so for the "Apples" in row `2` we enter the formula `=$B2*$C2`. In PhpSpreadsheet, we would set this formula in cell `D2` using:
177+
```php
178+
$spreadsheet->getActiveSheet()->setCellValue('D2','=$B2*$C2');
179+
```
180+
and then do the equivalent for rows `3` to `6`.
181+
182+
To calculate the "Total", we would use a different formula, telling it to calculate the sum value of rows 2 to 6 in the "Cost" column:
183+
184+
![12-CalculationEngine-Basic-Formula-2.png](./images/12-CalculationEngine-Basic-Formula-2.png)
185+
186+
I'd imagine that most developers are familiar with this: we're setting a formula that uses an Excel function (the `SUM()` function) and specifying a range of cells to include in the sum (`$D$2:$D6`)
187+
```php
188+
$spreadsheet->getActiveSheet()->setCellValue('D7','=SUM($D$2:$D6');
189+
```
190+
However, we could have specified an alternative formula to calculate that result, using the arrays of the "Quantity" and "Cost" columns multiplied directly, and then summed together:
191+
192+
![12-CalculationEngine-Array-Formula.png](./images/12-CalculationEngine-Array-Formula.png)
193+
194+
Entering the formula `=SUM(B2:B6*C2:C6)` will calculate the same result; but because it's using arrays, we need to enter it as an "array formula". In MS Excel itself, we'd do this by using `Shift-Ctrl-Enter` rather than simply `Enter` when we define the formula in the formula edit box. MS Excel then shows that this is an array formula in the formula edit box by wrapping it in the `{}` braces (you don't enter these in the formula yourself; MS Excel does it).
195+
196+
If we want to create an array formula in PhpSpreadsheet, we also have to indicate that it is an array formula.
197+
```php
198+
$spreadsheet->getActiveSheet()->setCellValue('D7','=SUM(B2:B6*C2:C6)', true);
199+
```
200+
We do this by providing a third argument in the call to `setCellValue()` that is only appropriate when setting a cell value to a formula, and that is the boolean value `true` passed as the `$isArrayFormula` argument. If the value we're setting in the cell isn't a formula value, then this additional argument will be ignored.
201+
202+
Or to identify the biggest increase in like-for-like sales from one month to the next:
203+
204+
![12-CalculationEngine-Array-Formula-3.png](./images/12-CalculationEngine-Array-Formula-3.png)
205+
```php
206+
$spreadsheet->getActiveSheet()->setCellValue('F1','=MAX(B2:B6-C2:C6)', true);
207+
```
208+
Which tells us that the biggest increase in sales between December and January was 30 more (in this case, 30 more Lemons).
209+
210+
---
211+
212+
These are examples of array formula where the results are displayed in a single cell; but other array formulae might be displayed across several cells.
213+
As an example, consider transposing a grid of data: MS Excel provides the `TRANSPOSE()` function for that purpose. Let's transpose our shopping list for the fruit:
214+
215+
![12-CalculationEngine-Array-Formula-2.png](./images/12-CalculationEngine-Array-Formula-2.png)
216+
217+
When we do this in MS Excel, we need to indicate ___all___ the cells that will contain the transposed data from cells `A1` to `D7`. We do this by selecting the cells where we want to display our transposed data either by holding the left mouse button down while we move with the mouse, or pressing `Shift` and using the arrow keys.
218+
Once we've selected all the cells to hold our data, then we enter the formula `TRANSPOSE(A1:D7)` in the formula edit box, remembering to use `Shift-Ctrl-Enter` to tell MS Excel that this is an array formula.
219+
220+
We also need to specify that selected range if we create the same array formula in PhpSpreadsheet.
221+
In addition to passing true for the `$isArrayFormula` argument, we also pass a fourth argument telling PhpSpreadsheet the range of cells that we want the transposed array to cover.
222+
```php
223+
$spreadsheet->getActiveSheet()->setCellValue('A10','=TRANSPOSE(A1:D7)', true, 'A10:G13');
224+
```
225+
Specifying this range tells PhpSpreadsheet (and MS Excel) that the result of the formula in cell `A10` is allowed to "spill" out into cells in that specified range; but also that it is limited to that range.
226+
If you don't specify a range, then PhpSpreadsheet will only apply this formula to a single cell.
227+
228+
Note also that we still set this as the formula for the top-left cell of that range, cell `A10`.
229+
230+
If you want to use other methods like `setCellValueExplicit()`, `setCellValueByColumnAndRow()` or `setCellValueExplicitByColumnAndRow()`, then the same additional arguments are also available with these methods.
231+
```php
232+
$spreadsheet->getActiveSheet()
233+
->setCellValueExplicit(
234+
'A10',
235+
'=TRANSPOSE(A1:D7)',
236+
\PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA,
237+
true,
238+
'A10:G13'
239+
);
240+
```
241+
242+
---
243+
244+
Excel365 introduced a number of new functions that return arrays of results.
245+
These include the `UNIQUE()`, `SORT()`, `SORTBY()`, `FILTER()`, `SEQUENCE()` and `RANDARRAY()` functions.
246+
While not all of these have been implemented by the Calculation Engine in PhpSpreadsheet, so they cannot all be calculated within your PHP applications, they can still be read from and written to Xlsx files.
247+
248+
The way these functions are presented in MS Excel itself is slightly different to that of other array functions.
249+
250+
The `SEQUENCE()` function generates a series of values (in this case, starting with `-10` and increasing in steps of `2.5`); and here we're telling the formula to populate a 3x3 grid with these values.
251+
252+
![12-CalculationEngine-Spillage-Formula.png](./images/12-CalculationEngine-Spillage-Formula.png)
253+
254+
Note that this is visually different to the multi-cell array formulae like `TRANSPOSE()`. When we are positioned in the "spill" range for the grid, MS Excel highlights the area with a blue border; and the formula displayed in the formula editing field isn't wrapped in braces (`{}`).
255+
256+
And if we select any other cell inside the "spill" area other than the top-left cell, the formula in the formula edit field is greyed rather than displayed in black.
257+
258+
![12-CalculationEngine-Spillage-Formula-2.png](./images/12-CalculationEngine-Spillage-Formula-2.png)
259+
260+
When we enter this formula in MS Excel, we don't need to select the range of cells that it should occupy; nor do we need to enter it using `Ctrl-Shift-Enter`. MS Excel identifies that it is a multi-cell array formula because of the function that it uses, the `SEQUENCE()` function (and if there are nested function calls in the formula, then it must be the outermost functionin the tree).
261+
262+
However, PhpSpreadsheet isn't quite as intelligent (yet) and doesn't parse the formula to identify if it should be treated as an array formula or not; a formula is just a string of characters until it is actually evaluated. If we want to use this function through code, we still need to specify that it is an "array" function with the `$isArrayFormula` argument, and the range of cells that it should cover.
263+
264+
```php
265+
$spreadsheet->getActiveSheet()->setCellValue('A1','=SEQUENCE(3,3,-10,2.5)', true, 'A1:C3');
266+
```
267+
268+
### The Spill Operator
269+
270+
If you want to reference the entire spillage range of an array formula within another formula, you could do so using the standard Excel range operator (`:`, e.g. `A1:C3`); but you may not always know the range, especially for array functions that spill across as many cells as they need, like `UNIQUE()` and `FILTER()`.
271+
To simplify this, MS Excel has introduced the "Spill" Operator (`#`).
272+
273+
![12-CalculationEngine-Spillage-Operator.png](./images/12-CalculationEngine-Spillage-Operator.png)
274+
275+
Using our `SEQUENCE()"`example, where the formula cell is `A1` and the result spills across the range `A1:C3`, we can use the Spill operator `A1#` to reference all the cells in that spillage range.
276+
In this case, we're taking the absolute value of each cell in that range, and adding them together using the `SUM()` function to give us a result of 50.
277+
278+
PhpSpreadsheet doesn't currently support entry of a formula like this directly; but interally MS Excel implements the Spill Operator as a function (`ANCHORARRAY()`). MS Excel itself doesn't allow you to use this function in a formula, you have to use the "Spill" operator; but PhpSpreadsheet does allow you to use this internal Excel function.
279+
280+
To create this same function in PhpSpreadsheet, use:
281+
```php
282+
$spreadsheet->getActiveSheet()->setCellValue('D1','=SUM(ABS(ANCHORARRAY(A1)))', true);
283+
```
284+
Note that this does need to be flagged as an array function with the `$isArrayFormula` argument.
285+
286+
When the file is saved, and opened in MS Excel, it will be rendered correctly.
287+
288+
289+
168290
## Locale Settings for Formulae
169291

170292
Some localisation elements have been included in PhpSpreadsheet. You can
@@ -201,7 +323,7 @@ $spreadsheet->getActiveSheet()->setCellValue('B8',$internalFormula);
201323
```
202324

203325
Currently, formula translation only translates the function names, the
204-
constants TRUE and FALSE, and the function argument separators. Cell addressing using R1C1 formatting is not supported.
326+
constants TRUE and FALSE (and NULL), Excel error messages, and the function argument separators. Cell addressing using R1C1 formatting is not supported.
205327

206328
At present, the following locale settings are supported:
207329

@@ -224,6 +346,8 @@ Russian | русский язык | ru
224346
Swedish | Svenska | sv
225347
Turkish | Türkçe | tr
226348

349+
If anybody can provide translations for additional languages, particularly Basque (Euskara), Catalan (Català), Croatian (Hrvatski jezik), Galician (Galego), Greek (Ελληνικά), Slovak (Slovenčina) or Slovenian (Slovenščina); please feel free to volunteer your services, and we'll happily show you what is needed to contribute a new language.
350+
227351
## Write a newline character "\n" in a cell (ALT+"Enter")
228352

229353
In Microsoft Office Excel you get a line break in a cell by hitting

0 commit comments

Comments
 (0)