A dynamic array formula is a formula that returns several values at once and spills them into the cells below and to the right of where you typed it. You enter one formula in one cell, and Excel fills as many cells as the answer needs. FILTER, SORT, UNIQUE and SEQUENCE are the best known; LET lets you name pieces of a formula. Dynamic arrays are available in Microsoft 365 and Excel 2021 or later. In Excel 2019 and earlier these functions do not exist.
They replace many jobs that used to need helper columns, pivot tables or Ctrl + Shift + Enter formulas. This guide shows what spilling means, then each function on one small sales table, how to combine conditions with AND and OR, and the errors you will meet (#SPILL! and #CALC!). Every result was produced by running the formula in Microsoft Excel.
In this guide
The short version
- One formula, many results: the result spills into the empty cells beside and below. Refer to the whole result with the
#sign, for exampleC1#. FILTER(range, condition)keeps rows,SORTorders them,UNIQUEremoves duplicates,SEQUENCEmakes number series.- In a
FILTERcondition,*means AND and+means OR. Brackets decide the meaning, as in any AND/OR rule. #SPILL!means something is in the way.#CALC!means the result is empty.
What spilling means
Type =A1:A3*2 in C1. Older Excel would multiply only A1 by 2 and you would have to copy the formula down. Modern Excel multiplies the whole range and shows all three results, each in its own cell. Only C1 holds a formula; the cells below display parts of its result. To refer to the whole result, add # after the anchor cell: C1#.
C1 holds =A1:A3*2, E1 holds =SUM(C1#), E2 holds =ROWS(C1#)
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 1 | 10 | 20 | 120 | ||
| 2 | 20 | 40 | 3 | ||
| 3 | 30 | 60 |
Only the anchor cell C1 contains a formula; C2 and C3 show its spilled results
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 1 | 10 | =A1:A3*2 | =SUM(C1#) | ||
| 2 | 20 | 40 | =ROWS(C1#) | ||
| 3 | 30 | 60 |
If a cell in the way of the spill is not empty, Excel refuses to overwrite it and shows #SPILL!. Here the same formula sits above a cell that contains an x:
| A | B | C | |
|---|---|---|---|
| 1 | 10 | #SPILL! | |
| 2 | 20 | x | |
| 3 | 30 |
Clear the blocking cell and the formula works at once. Other causes of #SPILL! are a merged cell in the way, an Excel Table (spills are not allowed inside tables), or a result too big for the sheet.
FILTER: pick the rows you want
FILTER(array, include, [if_empty]) returns the rows of array for which include is TRUE. The include argument is a test that produces one TRUE or FALSE per row, such as B2:B11="North". The sales table is in A1:E11 (the same one as in the SUMIFS guide), and the formulas below are typed into G2 with the column names copied into G1:K1:
=FILTER(A2:E11, B2:B11="North")
| G | H | I | J | K | |
|---|---|---|---|---|---|
| 1 | Date | Region | Product | Amount | Paid |
| 2 | 2024-01-05 | North | Pen | 500 | Yes |
| 3 | 2024-02-02 | North | Book | 700 | Yes |
| 4 | 2024-03-03 | North | Bag | 400 | Yes |
| 5 | 2024-03-30 | North | Pen | 350 | Yes |
The result is five columns wide, because the array we passed was five columns wide, and as tall as the number of matches.
When nothing matches, FILTER returns the error #CALC!. Give it a third argument for that case, or wrap it in IFERROR. The other ways to use it inside a formula, for example to add up the filtered amounts, are shown here:
| Formula | Excel shows | Why |
|---|---|---|
| =ROWS(FILTER(A2:E11,(B2:B11="North")*((C2:C11="Pen")+(C2:C11="Bag")))) | 3 | how many rows: North and (Pen or Bag), explained next |
| =ROWS(FILTER(A2:E11,((B2:B11="North")*(C2:C11="Pen"))+(C2:C11="Bag"))) | 5 | how many rows: (North and Pen) or Bag |
| =ROWS(FILTER(A2:E11,(B2:B11="North")+(C2:C11="Bag"))) | 6 | how many rows: North or Bag |
| =SUM(FILTER(D2:D11,D2:D11>=500)) | 2700 | add the filtered amounts, here 500, 700, 900 and 600 |
| =FILTER(D2:D11,D2:D11>1000,"nothing over 1000") | nothing over 1000 | the third argument replaces an empty result |
| =FILTER(D2:D11,D2:D11>1000) | #CALC! | no row is over 1000, so #CALC! |
| =IFERROR(FILTER(D2:D11,D2:D11>1000),"none") | none | IFERROR is the alternative |
| =FILTER(D2:D11,B2:B10="North") | #VALUE! | the include array must have the same height as the array (B2:B10 is one row short): #VALUE! |
FILTER with AND and OR
Inside FILTER, each condition gives a column of TRUE and FALSE (which count as 1 and 0). Multiply two conditions and the row must satisfy both: AND. Add two conditions and a row passes when at least one is true: OR. Brackets decide the grouping, so write the rule in words first. “North and (Pen or Bag)” keeps the region fixed and lets the product vary:
=FILTER(A2:E11, (B2:B11="North") * ((C2:C11="Pen") + (C2:C11="Bag")))
| G | H | I | J | K | |
|---|---|---|---|---|---|
| 1 | Date | Region | Product | Amount | Paid |
| 2 | 2024-01-05 | North | Pen | 500 | Yes |
| 3 | 2024-03-03 | North | Bag | 400 | Yes |
| 4 | 2024-03-30 | North | Pen | 350 | Yes |
Move the brackets and the rule changes. ((B2:B11="North")*(C2:C11="Pen")) + (C2:C11="Bag") means “(North and Pen) or any Bag”, which returns five rows instead of three, because every Bag row now passes whatever its region. Similarly (B2:B11="North")+(C2:C11="Bag"), plain OR, returns six rows. Both counts are in the table above (rows 1 to 3).
SORT and SORTBY
SORT(array, [sort_index], [sort_order], [by_col]) sorts a block of rows by one of its columns. The order is 1 for ascending and -1 for descending. SORTBY(array, by_array, order, ...) sorts by a column that need not be part of the result, with as many keys as you like. Sorting the sales by amount, biggest first:
=SORT(A2:E11, 4, -1)
| G | H | I | J | K | |
|---|---|---|---|---|---|
| 1 | Date | Region | Product | Amount | Paid |
| 2 | 2024-02-27 | South | Pen | 900 | No |
| 3 | 2024-02-02 | North | Book | 700 | Yes |
| 4 | 2024-03-15 | East | Book | 600 | Yes |
| 5 | 2024-01-05 | North | Pen | 500 | Yes |
Give two keys as array constants to sort by region and then by amount, descending, in the same call. Ties keep their original order:
=SORT(B2:D11, {1,3}, {1,-1})
| G | H | I | |
|---|---|---|---|
| 1 | East | Book | 600 |
| 2 | East | Bag | 250 |
| 3 | East | Pen | 200 |
| 4 | North | Book | 700 |
| 5 | North | Pen | 500 |
| 6 | North | Bag | 400 |
| 7 | North | Pen | 350 |
| 8 | South | Pen | 900 |
| 9 | South | Book | 300 |
| 10 | South | Bag | 150 |
SORTBY can return one column ordered by another. Column G lists the regions ordered by amount, and column H the amounts ordered by region and then amount, without showing the sort column at all:
| G | H | |
|---|---|---|
| 1 | South | 600 |
| 2 | North | 250 |
| 3 | East | 200 |
| 4 | North | 700 |
| 5 | North | 500 |
| 6 | North | 400 |
| 7 | South | 350 |
| 8 | East | 900 |
| 9 | East | 300 |
| 10 | South | 150 |
UNIQUE: distinct values and a live summary
UNIQUE(array, [by_col], [exactly_once]) returns the distinct rows of a range. Use it to list the regions without typing them. Because UNIQUE spills, another formula can refer to its result with #, which gives a summary table that grows and shrinks with your data, without a pivot table:
G1: =UNIQUE(B2:B11)
H1: =SUMIFS(D2:D11, B2:B11, G1#)
I1: =COUNTIFS(B2:B11, G1#)
| G | H | I | |
|---|---|---|---|
| 1 | North | 1950 | 4 |
| 2 | South | 1350 | 3 |
| 3 | East | 1050 | 3 |
Add a new region to the data and a new row appears in the summary. A few more counting patterns:
| Formula | Excel shows | Why |
|---|---|---|
| =COUNTA(UNIQUE(B2:B11)) | 3 | how many different regions |
| =ROWS(UNIQUE(B2:C11)) | 9 | different region and product pairs |
| =ROWS(UNIQUE(B2:C11,,TRUE)) | 8 | exactly_once TRUE: only pairs that appear once (North and Pen appears twice, so it drops out) |
| =TEXTJOIN(", ",TRUE,SORT(UNIQUE(C2:C11))) | Bag, Book, Pen | a sorted list of the products, joined into one text |
| =ROWS(UNIQUE(C2:C11)) | 3 | the number of different products |
| =SUMPRODUCT(1/COUNTIF(B2:B11,B2:B11)) | 3 | the classic way to count distinct values before UNIQUE existed |
SEQUENCE, TAKE and friends
SEQUENCE(rows, [columns], [start], [step]) creates a series of numbers, which is handy for row numbers, calendars and test data. Because it is an array, it can be used in arithmetic: SEQUENCE(5)^2 gives the squares. Here A1 holds SEQUENCE(2,3), E1 holds a weekly series of dates and G1 the squares:
| A | B | C | D | E | F | G | |
|---|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 3 | 2024-01-01 | 1 | ||
| 2 | 4 | 5 | 6 | 2024-01-08 | 4 | ||
| 3 | 2024-01-15 | 9 | |||||
| 4 | 2024-01-22 | 16 | |||||
| 5 | 25 |
Microsoft 365 also has functions that cut and join arrays: TAKE and DROP keep or remove rows from the top or bottom, and VSTACK and HSTACK stack arrays vertically or horizontally:
| Formula | Excel shows | Why |
|---|---|---|
| =TEXTJOIN(", ",TRUE,TAKE(SORT(D2:D11,,-1),3)) | 900, 700, 600 | the three biggest amounts |
| =TEXTJOIN(", ",TRUE,DROP(D2:D11,7)) | 150, 350, 250 | the amounts after dropping the first seven |
| =ROWS(VSTACK(A2:A11,A2:A11)) | 20 | two ranges stacked: 20 rows |
| =COLUMNS(HSTACK(A2:A11,D2:D11)) | 2 | two columns side by side |
LET: name the parts of a formula
LET(name1, value1, [name2, value2, ...], calculation) gives names to intermediate results inside one formula. It makes long formulas readable, and Excel computes each named part only once. The names exist only inside the LET:
| Formula | Excel shows | Why |
|---|---|---|
| =LET(total,SUM(D2:D11),n,COUNT(D2:D11),total/n) | 435 | the average, built from a named total and a named count |
| =LET(x,D2:D11,SUM(FILTER(x,x>=500))) | 2700 | the name x is reused twice, so the range is written once |
| =LET(a,5,b,a*2,a+b) | 15 | a name can use an earlier name |
| =LET(zz,5,zz)+zz | #NAME? | the name zz does not exist outside the LET: #NAME? |
| =LET(r,B2:B11,SUMPRODUCT(–(r="North"))) | 4 | the number of North rows |
Array arithmetic and its traps
In modern Excel a comparison on a range produces an array of TRUE and FALSE, and arithmetic on that array works on all the values at once. SUM((range>2)*range) is therefore a one-cell conditional total. In Excel 2019 and earlier the same formula needs Ctrl + Shift + Enter, or SUMPRODUCT, which handles arrays without it. A1:A5 hold 1, 3, 5, 7 and 9:
| Formula | Excel shows | Why |
|---|---|---|
| =SUM((A1:A5>2)+(A1:A5<8)) | 8 | trap: adding two conditions counts the values that pass BOTH twice (3, 5 and 7 give 2 each), so 8, not the 3 numbers between 2 and 8 |
| =SUM((A1:A5>2)*(A1:A5<8)) | 3 | multiply for AND: exactly the three values between 2 and 8 |
| =SUM(–((A1:A5>2)+(A1:A5<8)>0)) | 5 | add for OR, then test for greater than 0 so nothing counts twice: all five values pass |
| =SUM(A1:A5*(A1:A5>4)) | 21 | the sum of the values above 4 |
| =SUMPRODUCT((A1:A5>4)*A1:A5) | 21 | the same with SUMPRODUCT, which works in every Excel version |
| =SUM(IF(OR(A1:A5>7),1,0)) | 1 | OR() collapses the whole array into ONE TRUE, so this counts 1 whatever the data |
| =SUM(–(A1:A5>7)) | 1 | counting the values above 7, correctly |
| =MAX(A1:A5*(A1:A5<8)) | 7 | the largest value below 8 |
| =SUM(A1:A5*{1;0;1;0;1}) | 15 | an array constant with 1 and 0 picks rows 1, 3 and 5 |
The two traps to remember are the double count when you add overlapping conditions, and the fact that OR and AND reduce an array to a single answer, so they cannot be used to test each row. Use + and * for that.
Errors and common mistakes
Mistake 1: typing over a spill range
The spilled cells below the anchor are not editable individually. Type into one of them and you get #SPILL! in the anchor. Clear the cell and the spill returns.
Mistake 2: expecting it to work in Excel 2019 or earlier
Older versions do not have FILTER, SORT, UNIQUE, SEQUENCE or LET. A workbook that uses them shows #NAME? when opened there. Share results as values, or use SUMPRODUCT, INDEX and helper columns for older users.
Mistake 3: mismatched sizes in FILTER
The include range must have exactly as many rows as the array. One row short gives #VALUE!, as in the table above.
Mistake 4: forgetting the if_empty argument
An empty result gives #CALC!, which then spreads into every formula that uses it. Add the third argument, or IFERROR.
Mistake 5: OR() and AND() inside FILTER
They return a single TRUE or FALSE for the whole array, so the filter keeps everything or nothing. Use * and +.
Cheat sheet
| Task | Formula |
|---|---|
| Rows that match | =FILTER(A2:E11, B2:B11="North", "none") |
| AND of two tests | =FILTER(rows, (test1) * (test2)) |
| OR of two tests | =FILTER(rows, (test1) + (test2)) |
| A and (B or C) | =FILTER(rows, (A) * ((B) + (C))) |
| Sort by a column | =SORT(rows, 4, -1) |
| Sort by another range | =SORTBY(list, keys, -1) |
| Distinct values | =UNIQUE(range) |
| Number of distinct values | =COUNTA(UNIQUE(range)) |
| Whole spilled result | =SUM(C1#) |
| Number series | =SEQUENCE(5) |
| Top three | =TAKE(SORT(range, , -1), 3) |
| Name parts of a formula | =LET(x, range, SUM(x) / COUNT(x)) |
Try it yourself
Use the sales table in A1:E11 and work out each answer first.
1. List all amounts of 500 or more, sorted from biggest to smallest, in one formula.
Show solution
=SORT(FILTER(D2:D11, D2:D11>=500), , -1)
| G | |
|---|---|
| 1 | Date |
| 2 | 900 |
| 3 | 700 |
| 4 | 600 |
| 5 | 500 |
FILTER picks the amounts, SORT orders them, and the empty second argument keeps the default sort column.
2. How many different products are in the table, and what are they?
Show solution
| Formula | Excel shows |
|---|---|
| =COUNTA(UNIQUE(C2:C11)) | 3 |
| =TEXTJOIN(", ",TRUE,UNIQUE(C2:C11)) | Pen, Book, Bag |
3. Write the FILTER include test for “North and (Pen or Bag)”, then for “(North and Pen) or Bag”. Do they return the same number of rows?
Show solution
The first is (B2:B11="North")*((C2:C11="Pen")+(C2:C11="Bag")) and returns 3 rows. The second is ((B2:B11="North")*(C2:C11="Pen"))+(C2:C11="Bag") and returns 5 rows. The bracket placement changes the rule.
4. Make a formula that shows the squares of 1 to 5 using only one cell.
Show solution
=SEQUENCE(5)^2 spills 1, 4, 9, 16 and 25, as in the SEQUENCE grid above.
Frequently asked questions
What is a dynamic array in Excel?
A formula that returns more than one value and spills them into neighbouring cells automatically. Only the first cell holds the formula. Dynamic arrays exist in Microsoft 365 and Excel 2021 or later.
What does #SPILL! mean in Excel?
Excel cannot show the whole result because something is in the way: a non-empty cell, a merged cell, an Excel Table, or a result that is too big. Clear the obstruction and the formula works.
What does the # sign mean in a formula like A1#?
It is the spill range operator: it refers to the whole block that the formula in A1 spilled into, however big it currently is.
How do I filter with two conditions in Excel?
Use FILTER with the conditions multiplied for AND: =FILTER(A2:E11, (B2:B11="North") * (D2:D11>=500)). Add them for OR.
Why does FILTER return #CALC!?
No row matched, so the result is an empty array. Give FILTER a third argument, for example "none", or wrap it in IFERROR.
How do I remove duplicates with a formula?
=UNIQUE(range) returns the distinct values. Add SORT around it for an ordered list.
Do FILTER, SORT and UNIQUE work in Excel 2019?
No. They need Microsoft 365 or Excel 2021 or later. In Excel 2019 use SUMPRODUCT, INDEX, helper columns or the Advanced Filter.
What is the LET function for?
LET names values inside a formula so you can reuse them, which makes long formulas shorter, easier to read and often faster.
Test yourself
Timed questions on Arrays and Dynamic Array Functions, with an explanation for every answer.