Excelgoodies logo +40 31 2299227

LEARN THIS HANDS ON

Microsoft Excel Reporting

. Live Online FILLING FAST
View all upcoming batches
From Legacy Array Formulas to Dynamic Arrays: Refactoring Workbooks Without Breaking the Business

From Legacy Array Formulas to Dynamic Arrays: Refactoring Workbooks Without Breaking the Business

Refactoring old array-heavy workbooks is risky: one wrong move and someone’s month-end report stops working. This guide shows how to move from legacy CSE (Ctrl+Shift+Enter) formulas to modern dynamic arrays safely, with patterns, examples, and a repeatable migration checklist.

If you want to turn these techniques into robust reporting models, combining them with structured models and dashboards, consider deepening your skills with a focused course on advanced Excel reporting workflows.


1. Why Touch Legacy Array Formulas At All?

If the workbook “works”, why refactor?

Because legacy array formulas:

  • Are hard to debug (one formula, many cells, hidden CSE magic)
  • Encourage copy-paste sprawl across entire ranges
  • Break easily when someone inserts a column or row in the wrong place
  • Don’t play nicely with Tables, structured references, or dynamic ranges

Dynamic arrays (DA) give you:

  • Single-source formulas that spill results automatically
  • Cleaner logic with functions like FILTER, UNIQUE, SORT, XLOOKUP
  • Better performance when you remove thousands of repeated formulas
  • Easier integration with Power Query / Power BI models later

The goal isn’t to rewrite everything in one go. The goal is to reduce risk, make the model easier to maintain, and keep the business running while you modernize.


2. Know What You’re Dealing With: Inventory First, Refactor Second

Before changing anything, map the legacy formulas.

2.1 Identify CSE (Ctrl+Shift+Enter) Formulas

Use a simple process:

  1. Press F5 → Special…
  2. Choose Formulas → tick Arrays
  3. Excel selects all array formulas on the sheet

Repeat for each critical sheet. Document:

  • Sheet name
  • Range(s)
  • Purpose (e.g., “Sales summary by region”)
  • Dependencies (named ranges, input sheets)

2.2 Classify the Pattern

Most legacy array formulas fall into a few patterns:

  • Multi-criteria lookup / aggregation
    • E.g. SUM(IF((Region="East")*(Product="A"),Amount))
  • De-duplication / unique lists
    • E.g. IF(COUNTIF($A$1:A1,A1)=1,A1,"")
  • Top N / ranking logic
    • E.g. LARGE(IF(Category="X",Amount),k)
  • Matrix operations
    • E.g. MMULT, TRANSPOSE, FREQUENCY

Classify each formula into one of these buckets. This makes it easier to apply a standard refactoring pattern.


3. Core Dynamic Array Tools That Replace Classic Arrays

You already know SUMIF, INDEX, MATCH. For refactoring, these DA functions are your main workhorses:

  • FILTER – return rows that match conditions
  • UNIQUE – distinct lists
  • SORT / SORTBY – ordering without helper columns
  • SEQUENCE – generate index vectors
  • XLOOKUP – cleaner lookups than INDEX+MATCH
  • LET – name sub-expressions inside a formula
  • BYROW / BYCOL – apply a calculation across rows/columns

A lot of old CSE formulas can be replaced with one dynamic array formula that spills.


4. Refactoring Pattern 1: Multi-Criteria SUM/COUNT Arrays

4.1 Typical Legacy CSE Example

You’ve seen this pattern in a thousand workbooks:

{=SUM(IF(($A$2:$A$1000="East")*($B$2:$B$1000="Product A"),$C$2:$C$1000))}
  • Column A: Region
  • Column B: Product
  • Column C: Amount

4.2 Dynamic Array Replacement

For a single result, you can often use SUMIFS:

=SUMIFS($C$2:$C$1000,$A$2:$A$1000,"East",$B$2:$B$1000,"Product A")

But the real win is when you’re generating a whole matrix, e.g. totals by Region and Product.

Step 1: Create unique lists

=UNIQUE($A$2:$A$1000)   // Regions
=UNIQUE($B$2:$B$1000)   // Products

Assume:

  • Regions spill starting in E3
  • Products spill horizontally starting in F2

Step 2: Build a dynamic matrix with BYROW/BYCOL

=LET(
    Regions, UNIQUE($A$2:$A$1000),
    Products, UNIQUE($B$2:$B$1000),
    Amounts, $C$2:$C$1000,
    regionMatrix,
        BYROW(Regions,
            LAMBDA(r,
                BYCOL(Products,
                    LAMBDA(p,
                        SUMIFS(Amounts,$A$2:$A$1000,r,$B$2:$B$1000,p)
                    )
                )
            )
        ),
    regionMatrix
)

This single formula spills a full Region x Product summary table. No CSE, no copy-paste.

Migration tip: keep the old CSE block alongside the new DA block while you test. Use a simple check:

=SUM(ABS(OldRange-NewRange))

If this returns 0, you’re aligned.


5. Refactoring Pattern 2: De-duplication and Unique Lists

5.1 Legacy Pattern

Classic unique list with helper logic, often as an array:

{=IFERROR(INDEX($A$2:$A$1000, MATCH(0, COUNTIF($D$1:D1, $A$2:$A$1000), 0)), "")}

This is fragile, slow, and unreadable to most users.

5.2 Dynamic Arrays: UNIQUE + SORT

Replace the entire block with:

=SORT(UNIQUE($A$2:$A$1000))

If you need unique combinations (e.g. Region + Product):

=SORT(UNIQUE(CHOOSE({1,2},$A$2:$A$1000,$B$2:$B$1000)))

This spills a 2-column list of distinct Region–Product pairs.

Change management tip: because dynamic arrays spill, make sure the spill range is clear. If users are used to typing under the old list, move the new list to a dedicated output area.


6. Refactoring Pattern 3: Complex Lookups and INDEX/MATCH Arrays

6.1 Legacy Array Version

Multi-criteria lookup using INDEX/MATCH as an array:

{=INDEX($C$2:$C$1000, MATCH(1, ($A$2:$A$1000=H2)*($B$2:$B$1000=I2), 0))}

6.2 Dynamic Array with XLOOKUP

XLOOKUP doesn’t directly support multi-criteria, but you can combine fields:

=LET(
    key, $A$2:$A$1000 & "|" & $B$2:$B$1000,
    lookupKey, H2 & "|" & I2,
    XLOOKUP(lookupKey, key, $C$2:$C$1000)
)

If you need all matching rows instead of a single result, use FILTER:

=FILTER($A$2:$D$1000, ($A$2:$A$1000=H2)*($B$2:$B$1000=I2))

This returns all rows where Region = H2 and Product = I2.

Risk control: some legacy formulas rely on the first match only, even if there are duplicates. Confirm with users whether they expect first match or all matches before switching to FILTER.


7. Refactoring Pattern 4: Array-Based Ranking and Top N

7.1 Legacy CSE Top N

Example: top 5 customers by sales using LARGE in an array:

{=LARGE(IF($B$2:$B$1000="Customer", $C$2:$C$1000), ROW(A1))}

Then another array to get names via INDEX.

7.2 Dynamic Arrays: SORT + TAKE

If you have a simple table SalesTbl with columns Customer, Region, Amount:

=LET(
    data, SalesTbl,
    sorted, SORT(data, 3, -1),   // sort by Amount descending (3rd column)
    TAKE(sorted, 5)
)

Or with a filter included:

=LET(
    data, SalesTbl,
    filtered, FILTER(data, SalesTbl[Region]="East"),
    sorted, SORT(filtered, 3, -1),
    TAKE(sorted, 5)
)

This spills a full Top 5 table, no extra helper columns, no CSE.


8. Safe Migration Strategy: Don’t Break the Business

You’re not just rewriting formulas; you’re changing a production system. Treat it like a small refactor in code.

8.1 Work on a Copy First

  1. Save a copy of the workbook with a clear suffix, e.g. SalesModel_DA_Test.xlsx.
  2. Lock the original in a read-only location while you experiment.

8.2 Migrate in Small, Testable Steps

For each refactor:

  1. Isolate: Move the existing array block to a separate sheet (_OldLogic) or keep it side-by-side.
  2. Implement: Add the dynamic array formula in a new area.
  3. Compare outputs:
    • For numeric outputs: =SUM(ABS(OldRange-NewRange))
    • For text outputs: =SUM(--(OldRange<>NewRange)) to count mismatches
  4. Test scenarios:
    • Edge cases (empty filters, new categories, new regions)
    • Typical month-end volumes

Only replace the old block after you’ve validated.

8.3 Preserve Interfaces

Users may have downstream formulas pointing at your outputs. When you swap old for new:

  • Keep range addresses the same where possible
  • If you must move outputs, consider named ranges that point to the new spill ranges

Example: if Summary!B5:B20 used to hold totals, and your new dynamic array spills into Summary!E5#, define a name:

  • Name: SalesTotals
  • Refers to: =Summary!$E$5#

Then update downstream formulas to use SalesTotals instead of hardcoded ranges. This makes future refactors easier.

8.4 Communicate Changes

Even if you’re the only “Excel person”, there’s always at least one stakeholder relying on that workbook.

Tell them:

  • What changed (e.g. “Region/Product summary now auto-expands when new products are added.”)
  • What they might notice (e.g. “Totals area moved from columns B:D to F:H.”)
  • How to escalate if they spot discrepancies

9. When NOT to Refactor to Dynamic Arrays (Yet)

Dynamic arrays are powerful, but they’re not always the right move right now.

Consider postponing if:

  • Users are on mixed Excel versions
    • Dynamic arrays require relatively recent Excel builds
    • If half the team is on older versions, they’ll see #NAME? or #SPILL!
  • The workbook is feeding a fragile integration
    • External systems that expect fixed ranges may break when spill ranges change size
  • The model is a candidate for Power Query / Power BI
    • If you’re planning to move heavy transformations into Power Query, don’t spend days refactoring formulas that will be retired soon

In those cases, focus on documenting current logic and adding minimal improvements (e.g. named ranges, comments) while you plan a bigger architecture change.


10. One Practical Takeaway: Start With One High-Impact Block

Don’t try to convert the entire workbook in one weekend. Pick one high-impact, high-pain array block and:

  1. Identify its pattern (sum, unique, lookup, ranking).
  2. Rebuild it with dynamic arrays and LET.
  3. Validate outputs side-by-side.
  4. Swap it in and monitor.

Once you’ve done this end-to-end once, you’ll have a repeatable pattern you can apply across the rest of your legacy models without putting the business at risk.

Excel Formulas

New

Next Batches Now Live

Power BIPower BI
SQLSQL
Power AppsPower Apps
Power AutomatePower Automate
Microsoft FabricMicrosoft Fabrics
AzureAzure Data Engineering
Explore Dates & Reserve Your Spot → Reserve Your Spot →