Beyond Excel: VBA and Database Manipulation

Jan 13 2011   2:00PM GMT

Dynamic Named Ranges in Excel – Overcoming Shortcomings

Craig Hatmaker Craig Hatmaker Profile: Craig Hatmaker

A couple of months ago, Guest blogger Yoav Ezer posted a piece including the concept of Dynamic Named Ranges (see Strategies for Speeding Spreadsheets).  Dynamic Named Ranges are ranges within Excel that have been named using “Name Manager” or “Define Name” and that can be expanded or contracted without having to change what the name refers to.  Once named, you can use the range’s name in formulas and data validation instead of the common “$A$1:$D$50″ cell references.  This makes your formulas simpler to read.  And with the dynamic nature of Dynamic Named Ranges, you don’t have to change the formula when rows of data are added. 

Here is a typical formula that could be added in Name Manger for a named range that starts in cell $A$1, has two columns, and can grow to as many rows as entries:

=OFFSET($A$1,0,0,COUNTA($A:$A),2)

Translating this formula for us humans, it says:

  • From $A$1
  • Go down 0 rows
  • Go right 0 columns
  • Expand to down by the number of cells in column A that contain characters
  • Expand to the right 2 columns 

Shortcommings

I “Googled” the subject and found many articles on Dynamic Named Ranges.  Obviously there is a lot of interest and many examples of their use; however, in every article I read, including those in Microsoft’s MSDN, I kept coming across the same shortcomings:

  • If your range contains empty cells, especially in the first column, you’re likely to get bad results
  • If your range contains spaces after the last row, you’re likely to get bad results.
  • If your range contains columns of different lengths, the suggested approaches are very cumbersome.
  • If your range starts somewhere other than row 1, the formula gets more complex.
  • If another range exists below or to the right of the first named range, you’re likely to get bad results.

Most of these limitations are because almost everyone seems to want to use COUNT or COUNTA to determine how many rows should be contained in the range.  I found one blogger who used MATCH instead of COUNT.  This had the advantage of skipping over empty cells, but still worked only for numbers, or for characters, but not for both (unless you double the formula and the MAX function). 

=OFFSET($A$1,0,0,MATCH("",$A:$A,-1),2)		'Finds last character cell
=OFFSET($A$1,0,0,MATCH(1E+306,$A:$A,1),2)	'Finds last numeric cell

Seeking A Better Approach

The above approaches all work.  But no one of them works for all circumstances by itself.  And none of the approaches dealt with the stray space after the table’s last row.  That’s not good enough.  I want one compact formula that requires as little thought as possble that works for as many situations as possible.  In researching and experimenting, I accidently stumbled on a quirk regarding formulas stored in names that makes overcoming these problems much simpler.  Chip explains this quirk very well.

Defined Name Formulas And Array Formulas by Charles H. Pearson

If you use a formula in a Defined Name, that formula is evaluated as if it were an array formula. There is no way to force a formula in a Defined Name to be evaluated as a non-array formula.

Brilliant!  With this bit of knowledge, we can use logical functions that are insensitive to the type of data used (Numbers vs Characters).  Here is an array formula that finds the last row in column A containing anything at all (NOTE! The curly brackets are the result of Shift-Ctrl-Enter.  For more information on how to enter array formulas see Array Formulas by Charles H. Pearson).

{=MAX(IF($A:$A<>"", ROW($A:$A),0))}

Building on this, we can find the last row within the first four columns that contains anything at all, regardless of which column is longest.

{=MAX(IF($A:$D<>"", ROW($A:$A),0))}

Because logical operators in Excel return 0 for FALSE and 1 for TRUE, we can shorten the formula up a bit, if that’s your preference.

{=MAX(($A:$D<>"")*ROW($A:$A))}

But one problem remains.  In my opinion, entries containing only spaces are the same as totally empty cells and as I said before, the above formula finds cells that contain anything at all, including those with just spaces.  No worries, this is simple enough to overcome by trimming cells.

{=MAX((TRIM($A:$D)<>"")*ROW($A:$A))}

This formula finds the last cell that contains anything other than just spaces.  It doesn’t matter if any of the cells in between are empty.  It doesn’t matter if any of the cells are numbers or characters.  It doesn’t matter which column is longest. To put it all together, you need to adjust for one more thing, the starting row.  I often want my ranges to start in row 4 with totals in row 2.  So if you want your range to start somewhere other than row 1, you need to subtract the starting row number and add 1 back for good measure.   Here is the final formula that you would enter into Name Manager for a range that starts in $A:$4 and has 5 columns (NOTE! Name Manager does not need the curly brackets since it treats ALL formulas as array formulas no matter what).

=OFFSET($A$4,0,0,MAX((TRIM($A:$E)<>"")*(ROW($A:$A)))-3, 5) 

This works.  It’s also slow.  It’s cumbersome.   

Solving with VBA

Formula approaches just don’t work well. There are just too many ways they can fail and too many limitations.  So I looked to VBA.  VBA has always had a very simple and elegant way of dealing with dynamic ranges:

Set DynamicRange = Range("A4").CurrentRegion

CurrentRegion finds all adjacent non-empty cells.  So if “A4″ is anywhere inside a table, CurrentRegion will identify the entire table.  One minor problem is it will also pick up any adjacent cells with stray spaces.  But a bigger problem is that when used in a UDF (User Defined Function intended to be used within an Excel formula), CurrentRegion returns only one cell. 

A different approach (copied from Andy Pope) can be encapsulated into a VBA routine and used in a UDF context:

Set DynamicRange = Range("A4").Resize(Range("A4").End(xlDown).Row - 1, _
                   Range("A4").End(xlToRight).column - 1)

End() works as long as there is more than one row and column in the range.  If, for example, only one row is in the table, the End() method will find the last cell in the worksheet or the next list.

The “One Best Way”

Going through this exercise was interesting, but utlimately, neither VBA, nor complex formulas are required to create dynamic ranges without ANY of the shortcommings of either.  We’ll discuss that after another great Excel tip from Yoav Ezer.

2  Comments on this Post

 
There was an error processing your information. Please try again later.
Thanks. We'll let you know when a new response is added.
Send me notifications when other members comment.

REGISTER or login:

Forgot Password?
By submitting you agree to receive email from TechTarget and its partners. If you reside outside of the United States, you consent to having your personal data transferred to and processed in the United States. Privacy
  • pmensch

    I am interested in figuring out an easy way to set up dynamic ranges

     

    10 pointsBadges:
    report
  • tomy2g
    it doesn't appear you addressed the issue of multiple dynamic ranges in the same column?
    10 pointsBadges:
    report

Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: