xlDynjamic Valid HTML 4.01!

 This article applies to all versions of Microsoft Excel For Windows®, unless stated otherwise.

Author(s): Frank Kabel & Bob Phillips
© Copyright, 2004, Frank Kabel & Bob Phillips

Date Written: 1st September 2004
Last Updated: 28th September 2004


Article Summary:

Finding specific values in a list or a data range is one of the more common tasks while developing a spreadsheet application. Many people are quite familiar using the standard Excel functions VLOOKUP/HLOOKUP or a combination of INDEX and MATCH to locate a value in a data table depending on a condition. A different problem is the location of the last filled element within a range (either in a column or a row). This paper discusses that problem, and offers various techniques, formulae, and consideration to apply.


Article Contents:

This paper is structured in the following way:

Introduction
Getting The Last Numeric Element In A Column/Row
Getting The Last Text Element In A Column/Row
Getting The Last Numeric or Text Element In A Column/Row
Worksheet Benchmarks
Worksheet Function Summary
Last Value In VBA
Acknowledgements and References

This article assumes familiarity with the following:


Introduction

As Excel has no dedicated function to get the last value in a range the following pages describe how to use the built-in functions for this purpose, separately addressing numeric and text data types, or any data type.

This paper also has a short section on VBA solutions for getting the last value in a range, but mainly focuses on worksheet function based solutions.

Within Excel there is not a single formula to get the last element within a column or a row, but it can be achieved by a combination of worksheet functions within a variety of differing requirements. This paper shows a number of the combinations, and discusses the strengths and weaknesses of each approach, and where it might be applied.

The fact that this is achieved in Excel by combining a number of functions can be seen as a strength of Excel (a strong base set of functions that provide a large degree of extensibility), or a weakness (lack of commonly required functions), depending upon your viewpoint.

Which formula you choose can depend on the following factors:

What follows is a compilation of several working formulas with a description to help you to choose depending on your needs.

In general the description focuses on getting the last value from a column. The formula to get the last value from a row is given but generally not described.

The examples mainly work upon a full column (A:A), or the best part of (A1:A65535), but it should be remembered that the formulae will apply to any range such as A1:An, where n is a row number between 1 and 65535.

Most formulae can deal with a full column , but the array formulae are restricted to a range that is a subset of a full column. This is a typical restriction of array formulas not being able to deal with 65536 elements (the maximum number of rows Excel supports), but only 65535 elements (the largest value which could be stored in an unsigned integer variable). Thus you have to use a range such as A1:An where n is an integer between 1 and 65535. This won't get the correct last element in a column if this resides in the row 65536 (but in most cases this will not be an issue). In all cases, a full row (1:1) can be specified as this fits easily with the limits of an unsigned integer variable.

top


Getting Last Numeric Value In Range

In some cases you only need to find the last numeric value, ignoring any non-numeric values.

#001 - Searching For Largest Possible Number Using INDEX/MATCH
#002 - Searching For Largest Possible Number Using LOOKUP
#003 - An INDEX Array Formula
#004 - LOOKUP Alternative

#001 - Searching For Largest Possible Number Using INDEX/MATCH

For the last numeric element in a column, a simple INDEX or LOOKUP will work
=INDEX(A:A,MATCH(9.99999999999999E307,A:A))

Similarly, for the last numeric element in a row:
=INDEX(1:1,MATCH(9.99999999999999E307,1:1))

Last Value 1
Fig. 1.

How it works:

Constraints:

top top

#002 - Searching For Largest Possible Number Using LOOKUP

An alternative is a similar approach using the LOOKUP function
=LOOKUP(9.99999999999999E307,A:A)

Similarly, for the last numeric element in a row:
=LOOKUP(9.99999999999999E307,1:1)

Last Value 2
Fig. 2.

How it works:

Constraints:

top top

#003 - An INDEX Array Formula

An alternative solution can be achieved with the following array formula
=INDEX(A:A,MAX(IF(ISNUMBER(A1:A65535),ROW(A1:A65535))))
or
=INDEX(1:1,1,MAX(IF(ISNUMBER(1:1),COLUMN(1:1))))

As this is an array formula, it is committed with Ctrl-Shift-Enter.

Last Value 3
Fig. 3.

How it works:

Constraints:

top top

#004 - LOOKUP Alternative

There's an additional alternative that doesn't ignore error values, but highlights them in the result returned:
=LOOKUP(MAX(A:A)+1,A:A)
or
=LOOKUP(MAX(1:1)+1,1:1)

Last Value 4
Fig. 4.

How it works:

Constraints:

top


Getting Last Text Value In Range

Using the techniques described for finding numeric values you could use similar formulas to return the last text entry within a range.

#005 - Searching For Largest Text Value Using INDEX/MATCH
#006 - Searching For Largest Text Value Using LOOKUP
#007 - An INDEX Array Formula
#008 - An INDEX Array Formula Alternative

#005 - Searching For Largest Text Vale Using INDEX/MATCH

=INDEX(A:A,MATCH(REPT("Z",255),A:A))
For the last text element in a column

or
=INDEX(1:1,MATCH(REPT("Z",255),1:1))

For the last text element in a row

Last Value 5
Fig. 5.

How it works:

Constraints:

top top

#006 - Searching For Largest Possible Number Using LOOKUP

=LOOKUP(REPT("Z",255),A:A)
For the last text element in a column

or

=LOOKUP(REPT("Z",255),1:1)
For the last text element in a row

Last Value 6
Fig. 6.

How it works:

Constraints:

top top

#007 - An INDEX Array Formula

An alternative solution can be achieved with the following array formula
=INDEX(A:A,MAX(IF(ISTEXT(A1:A65535)*(A1:A65535<>""),ROW(A1:A65535))))
or
=INDEX(1:1,1,MAX(IF(ISTEXT(1:1)*(1:1<>""),COLUMN(1:1))))

As this is an array formula, it is committed with Ctrl-Shift-Enter.

Last Value 7
Fig. 7.

How it works:

Constraints:

top top

#008 - An INDEX Array Formula Alternative

To ignore errors in the range, this slight variation on the array formula works.
=INDEX(A:A,MAX(IF(ISTEXT(A1:A65535),ROW(A1:A65535))))
or
=INDEX(1:1,1,MAX(IF(ISTEXT(1:1),COLUMN(1:1))))

As this is an array formula, it is committed with Ctrl-Shift-Enter.

Last Value 8
Fig. 8.

How it works:

Constraints:

top


Getting Last Numeric or Text Value In Range

Searching for any data type within the range gives rise to another set of formulae.

#009 - Using OFFSET And COUNTA
#010 - Using An INDEX Array Formula
#011 - Using An INDEX Array Formula - 2
#012 - Using A Non-Array LOOKUP Formula
#013 - Using A Non-array INDEX/MATCH Formula
#014 - Using A Non-array INDEX/MATCH Formula

#009 - Using OFFSET And COUNTA

A simple and common formula to find any last value is to use a combination of OFFSET and COUNTA. e.g.
=OFFSET($A$1,COUNTA(A:A)-1,0)
for searching within a column

or
=OFFSET($A$1,0,COUNTA(1:1)-1)
for searching within a row.

Last Value 9
Fig. 9.

How it works:

Constraints:

top top

#010 - Using An INDEX Array Formula

Solution #009 cannot handle embedded blank cells in the range, so if this is a consideration , a different solution is required.

One solution is using a combination of INDEX, ISBLANK & ROW (or COLUMN if processing a single row).
=INDEX(A:A,MAX(IF(ISBLANK(A1:A65535),0,ROW(A1:A65535))))
for searching within a column

or
=INDEX(1:1,1,MAX(IF(ISBLANK(1:1),0,COLUMN(1:1))))
for searching within a row.

These are array formulae, so they are committed with Ctrl-Shift-Enter.

Last Value 10
Fig. 10.

How it works:

Constraints:

top top

#011 - Using An INDEX Array Formula - 2

If you want to highlight any errors within your range you can slightly adapt the above formulae to
=INDEX(A:A,MAX(IF(A1:A65535<>"",ROW(A1:A65535))))
or
=INDEX(1:1,1,MAX(IF(1:1<>"",COLUMN(1:1))))

These are array formulae, so they are committed with Ctrl-Shift-Enter.

Last Value 11
Fig. 11.

How it works:

Constraints:

top top

#012 - Using A Non-Array LOOKUP Formula

A non-array solution can be found using the LOOKUP function
=LOOKUP(2,1/(1-ISBLANK(A1:A65535)),A1:A65535)
for searching within a column
or
=LOOKUP(2,1/(1-ISBLANK(1:1)),1:1)
for searching within a row.

Last Value 12
Fig. 12.

How it works:

Constraints:

top top

#013 - Using A Non-array INDEX/MATCH Formula

If you don't want to return formula results such as "" as last element within your range you can slightly adapt the above formulas to
=LOOKUP(2,1/(A1:A65535<>""),A1:A65535)
for searching within a column

or
=LOOKUP(2,1/(1:1<>""),1:1)
for searching within a row.

Last Value 13
Fig. 13.

How it works:

Constraints:

top top

#014 - Using A Non-Array LOOKUP Formula

The previous two solutions (#009 and #010) have one big disadvantage, they are slow As an alternative you can use the following non-array formula
=INDEX(A:A,MAX(MATCH({"ZZZZZZZZZZZZZZZZZZZZ",9.99999999999999E+307},A:A)))
for searching within a column
or
=INDEX(1:1,1,MAX(MATCH({"ZZZZZZZZZZZZZZZZZZZZ",9.99999999999999E+307},1:1)))
for searching within a row.

Last Value 14
Fig. 14.

How it works:

Constraints:

top


Worksheet Functions Benchmarks

In many instances, formula performance will not be an issue, and so the choice of which formula to use would be made on the values to be tested, error handling requirements, nested function restrictions and personal preference.

In some situations, the complexity of the spreadsheet will be such that it requires a number of formulas, and inevitably, more formulas will mean a slow-down in the re-calculation of the spreadsheet. In these cases, performance becomes an issue, so you need to understand the relative performance benchmarks of the differing formulas.

The benchmark for the formulas described in this paper was created using FastExcel V2, using the following environment:-

The 13 formulae given above are benchmarked. In the table, Fig. 14, which shows the result of the benchmark, they are referenced by the id number, which are all listed below with the appropriate formula:-

001 - INDEX(A:A,MATCH(9.99999999999999E307,A:A))
002 - LOOKUP(9.99999999999999E307,A:A)
003 - INDEX(A:A,MAX(IF(ISNUMBER(A1:A65535),ROW(A1:A65535))))
004 - LOOKUP(MAX(A:A)+1,A:A)
005 - INDEX(A:A,MATCH(REPT("Z",255),A:A))
006 - LOOKUP(REPT("Z",255),A:A)
007 - INDEX(A:A,MAX(IF(ISTEXT(A1:A65535)*(A1:A65535<>""),ROW(A1:A65535))))
008 - INDEX(A:A,MAX(IF(ISTEXT(A1:A65535),ROW(A1:A65535))))
009 - OFFSET($A$1,COUNTA(A:A)-1,0)
010 - INDEX(A:A,MAX(IF(ISBLANK(A1:A65535),0,ROW(A1:A65535))))
011 - INDEX(A:A,MAX(IF(A1:A65535<>"",ROW(A1:A65535))))
012 - LOOKUP(2,1/(1-ISBLANK(A1:A65535)),A1:A65535)
013 - LOOKUP(2,1/(A1:A65535<>""),A1:A65535)
014 - INDEX(A:A,MAX(MATCH({"ZZZZZZZZZZZZZZZZZZZZ",9.99999999999999E+307},A:A)))
Benchmark
Fig. 14.

top


Worksheet Functions Summary

This section summarises the functions and formula combinations described above, and the various facts, attributes, etc. of each, and then summarises what has been described before.

Solution
Id
Numeric (N)
/ Text (T) /
Any (A)
Full Column
Handled
(Y/N)?
Conti-
guous
(Y/N)?
Ignore (I) or
Highlight (H)
Errors
Array Formula
(Y/N)?
Function
Calls
Benchmark
Rating
Group/Overall
001 N Y N I N 2 2/2
002 N N N I N 1 1/1
003 N N N I Y 5 4/10
004 N Y N H N 2 3/7
   
005 T Y N I N 3 2/4
006 T Y N I N 2 1/3
007 T N N H Y 5 4/14
008 T N N I Y 5 3/9
   
009 A Y Y I N 2 2/6
010 A N N I Y 5 4/11
011 A N N H Y 4 3/8
012 A N N I N 2 6/13
013 A N N I N 1 5/12
014 A Y N I N 3 1/5
Fig. 15.

Conclusions

  1. General observations
    • The worst performance is achieved with the error highlighting text search formula (007), over twice as slow as any other
    • The LOOKUP formulas (012 and 013) for searching for any value have the worst overall performance. They are even slower than array formula alternatives
    • The array formulas (003, 008, 010 and 011) are nearly as slow as the LOOKUP formulas
    • The remaining formulas (001, 002, 005, 006 and 013) are all more than 200 times faster than the other formulas.


  2. Searching for the last numeric value
    • The best was measured for the LOOKUP formula (002). The other INDEX/MATCH formula (001) using a similar approach is nearly as fast as the LOOKUP variant
    • Both formulas (001 and 002) use the same approach, that is searching for the 'big number' 9.99999999999999E307
    • The INDEX/MAX/ISNUMBER formula combination (003) is more than 360 times slower than the fastest LOOKUP formula (002)
    • The alternative LOOKUP/MAX that highlights errors is over 20 times as slow as the fastest formula (002)
    • The initial INDEX/MATCH (001), the LOOKUP (002) formulas, and the INDEX/MAX/ISNUMBER ()03) formula all ignore error values, whereas the LOOKUP/MAX (004) formula highlights errors.


  3. Searching for the last text value:
    • The INDEX/MATCH/REPT and the LOOKUP/REPT formulas (005 and 006) beat the array INDEX/MAX/ISTEXT formula (007) solution by more than 300 times
    • The INDEX/MATCH/REPT, the LOOKUP/REPT and the INDEX/MAX/ISTEXT formula (005, 006 and 008) all ignore error values.


  4. 4. Searching for any value:
    • In this case, using either the array formula INDEX/MAX/ISBLANK (010) or either of the LOOKUP formula combinations (012 and 013) will result in a slow performance
    • The Fastest formula is the INDEX/MAX/MATCH combination (014). This is consistent with the results from the other search functions for finding only numeric or text values. It is approximately two times slower than the variants for finding a single data type but it is still nearly 200 times faster than the other 3 formula alternatives for finding the last value of either text or numeric data< /li>
    • The OFFSET formula ((009), the two array INDEX formulas (010 and 011), and the INDEX/MAX/MATCH combination (014) all ignore error values. The INDEX/MAX/ROW formula (011) and the LOOKUP formula (013) both highlight errors.

Recommendations

In general, recommendations are dificult to make, as every individual need will differ, and the poor solution in one situation may be ideal in another.

The conclusions above are provided to help you to choose the appropriate formula for your particular circumstances.

top


Last Value In VBA

Simple Last Value

Finding the last value in a column is straight-forward. Assuming that the column to be searched is column A, this code will return the final cell

Dim cLastRow As Long
Dim oLast As Range

    cLastRow = Cells(Rows.Count,"A").End(xlUp).Row
    Set oLast = Range("A" & cLastRow)
    Activecell.Value = oLast.Value
				

This works by starting at the bottom-most row in the column, Cells(Rows.Count,"A"), and then moving up to the first found non-empty cell, End(xlUp), and get the row number for that cell, .Row.

This row number is then used in the Range statement to get that cell object.

Similalrly, to get the last value in a row 1, use

Dim cLastCol As Long
Dim oLast As Range

    cLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
    Set oLast = Cells(1,cLastCol)
    Activecell.Value = oLast.Value
				

This is a similar technique, going to the last column in the row, and moving back left.

Note that this uses Cells rather than Range as the column returned is a number.

This can be turned into a generic function that works for either row, or column.

Function fzLastValue(RangeId As Variant, Optional byColumn As Boolean = True)
Dim cLast As Long
Dim oLast As Range

    Application.Volatile

    If byColumn Then
        cLast = Cells(Rows.Count, RangeId).End(xlUp).Row
       Set oLast = Range(RangeId & cLast)
    Else
        cLast = Cells(RangeId, Columns.Count).End(xlToLeft).Column
        Set oLast = Cells(RangeId, cLast)
    End If

    fzLastValue = oLast.Value

End Function
				

Application.Volatile is needed if this function is to be used as a worksheet function to ensure that the calling cell gets updated if the range being searched is changed in any way.

This will find the last value, regardless of whether it is text, numeric, or an error value.

Last Numeric Or Text Value

Getting the last numeric or text value is somewhat more difficult. One way is to use some of the worksheet functions described above, called from VBA.

This can all be amalgamated as before intro a single function, as before, which can be caled as a worksheet function.

Function fzLastValue(RangeId As Variant, _
                     Optional byColumn As Boolean = True, _
                     Optional ValueType As String = "A", _
                     Optional Highlight As Boolean = False)
Dim cLast As Long
Dim oLast As Range

    Application.Volatile

    Select Case UCase(ValueType)

        Case "N":
            If byColumn Then
                If Highlight Then
                    fzLastValue = LastError(rng, byColumn, ValueType)
                Else
                    fzLastValue = Cells(Evaluate("MATCH(" & _
                     "9.99999999999999E307," & rng.Address & ")"), rng.Column)
                End If
            Else
                If Highlight Then
                    fzLastValue = LastError(rng, byColumn, ValueType)
                Else
                    fzLastValue = Cells(rng.Row, Evaluate("MATCH(" & _
                       "9.99999999999999E307," & rng.Address & ")")).Value
                End If
            End If

        Case "T":
            If byColumn Then
                If Highlight Then
                    fzLastValue = LastError(rng, byColumn, ValueType)
                Else
                    fzLastValue = Cells(Evaluate("MATCH(REPT( & _
                        ""Z"",255)," & rng.Address & ")"), & _
                        rng.Column).Value
                End If
            Else
                If Highlight Then
                    fzLastValue = LastError(rng, byColumn, ValueType)
                Else
                    fzLastValue = Cells(rng.Row, Evaluate("MATCH(REPT( & _
                       ""Z"",255)," & rng.Address & ")")).Value
                End If
            End If

        Case Else:
            If byColumn Then
                If Highlight Then
                    fzLastValue = LastError(rng, byColumn, ValueType)
                Else
                    cLast = Cells(Rows.Count, rng.Column).End(xlUp).Row
                    fzLastValue = Cells(cLast, rng.Column).Value
                End If
            Else
                If Highlight Then
                    fzLastValue = LastError(rng, byColumn, ValueType)
                Else
                    cLast = Cells(rng.Row, Columns.Count) & _
                    	.End(xlToLeft).Column
                    fzLastValue = Cells(rng.Row, cLast).Value
                End If
            End If

    End Select

End Function

Private Function LastError(rng As Range, _
                           byColumn As Boolean, _
                           ValueType As String)
Dim cLast As Long
Dim vText

    Select Case UCase(ValueType)

        Case "N":
            If byColumn Then
                vText = Evaluate("LOOKUP(MAX(" & rng.Address & ")+1," & _
                    rng.Address & ")")
                LastError = vText
            Else
                LastError = Evaluate("LOOKUP(MAX(" & rng.Address & ")+1," & _
                    rng.Address & ")")
            End If

        Case "T":
            vText = Evaluate("LOOKUP(MAX(" & rng.Address & ")+1," & _
                rng.Address & ")")
            If VarType(vText) <> 10 Then
                vText = Evaluate("LOOKUP(REPT(""Z"",255)," & rng.Address & ")")
            End If
            LastError = vText

        Case Else:
            vText = Evaluate("LOOKUP(MAX(" & rng.Address & ")+1," & _
                rng.Address & ")")
            If VarType(vText) <> 10 Then
                If byColumn Then
                    cLast = Cells(Rows.Count, rng.Column).End(xlUp).Row
                    vText = Cells(cLast, rng.Column).Value
                Else
                    cLast = Cells(rng.Row, Columns.Count).End(xlToLeft).Column
                    vText = Cells(rng.Row, cLast).Value
                End If
            End If
            LastError = vText

    End Select

End Function
				

This is not necessarily intended as a complete working solution UDF, but is more to show how to achieve each of the different solutions, to be deployed as befits in VBA.

The performance of this function is very poor, so that should deter deployment in a live environment, but the techniques used are still valid.

top


References

Further Reading

1. More about array formulas:

2. More about finding the last element in a range (including VBA solutions)

top


Acknowledgments

These formula combinations have been harvested from a number of sources, reference websites, solutions provided in the Excel newsgroups, and the authors own development experiences. This means that you get a comprehensive toolkit presented that you can utilise, with most of the research already done for you.

With this extensive collation of items, acknowledgement of the contribution of all of the individuals in the development of these formulas is not possible, but we would give mention to the contribution of the following individuals (in no order other than alphabetic)

In addition, mention is given to Charles Williams for the FastExcel utility used in the benchmarks.

top


Downloads

This page can be downloaded as a zipped Word document here download (307Kb)