This post is going to look at how to return an array from a udf. My last post looked at how to return a range from a UDF and in that, I included a small, bonus function which gave you the interior color of a cell.
I'm going to modify that function so it becomes an array function, or an array formula as they are also known. Specifically it will become a multi-cell array function as it will return multiple values into multiple cells.
What is an Array?
An array is just a list of values, e.g. 1, 2, 3, 4, 5 is an array with 5 values, or elements
That was an example of a one dimensional array. Think of one dimension representing values along a straight line, like the x-axis on a chart.
A two dimensional array holds values that can be used to represent something that requires two things to identify it, e.g. the x and y co-ordinates on a chart, the squares on a chess board, or even the cells on your worksheet (A1, etc).
Three dimensional arrays contain three lists of values used to identify something (the xyz co-ordinates) perhaps the latitude, longitude and height above sea level of a point on the Earth's surface.
You can have more dimensions of course if you need them, but for my example we're sticking with just one.
Returning Arrays of Different Sizes
In order to process an unknown number of values, we need to work out how many values are passed into our function and then allocate the appropriate amount of storage in an array.
Initially we allocate an unknown amount of storage space for the array named Result():
Dim Result() As Variant
Once the function is called we can work out how many cells are in the range we're passing into the function. We can do this by using Application.Caller which gives us a Range object, as long as our function is called from a cell. This range is the range of cells passed into the function.
We can work out how many cells in the range and whether we're passing a row or a column into the function
With Application.Caller CallerRows = .Rows.Count CallerCols = .Columns.Count End With
If CallerRows > 1 then a column is being passed into the function.
If CallerCols > 1 then a row is being passed into the function.
The number of cells in the range is CallerRows * CallerCols, so we redimension the array to hold a value for each of those cells.
'ReDimension the array to fit all the elements (cells) ReDim Result(1 To CallerRows * CallerCols)
If we pass in a range containing 6 cells, then our result will contain 6 values. Remember that this function is only dealing with one dimensional arrays, that is, values either in a row or a column.
If we pass in a range containing 10 cells, but allocate less than 10 cells for the result, we'll get #VALUE! errors.
If we pass in 6 cells, and allocate more than 6 cells for the result, cells 7 onwards will be filled with 0.
How you choose to deal with this is up to. You can write your own error handling, or just let Excel deal with it.
Don't Forget
This is an array formula, so when you enter it you must use CTRL+SHIFT+ENTER. That is, hold down CTRL and SHIFT, then press ENTER.
Fixed Sized Arrays
If you know the number of elements in your array will be fixed you can specify this and not worry about working it out when the function is called, For example, here's a 10 element array:
Dim myResult(1 To 10) As Variant
But passing in a range with more or less than 10 elements could easily break your function.
Array Orientation
By default a function will return an array as a row. If you want the results to go into a column, you need to transpose the array
'Transpose the result if returning to a column If CallerRows > 1 Then GetColor = Application.Transpose(Result) Else GetColor = Result End If
Download the Workbook
Enter your email address below to download the sample workbook.
Here's a link to download a workbook with sample code and examples.
Stephen
Interesting, thanks. Your solution is well on the way towards something that has always bothered me …
When charting data from a list or table, there is no way to accumulate the values as part of the charting process. OK, I obviously just insert a helper column that accumulates the values and chart that. Simple enough, but you’d think there would be an option built into Excel’s charting system.
Helper columns always seem like clutter to me, especially for something so simple. So I attempted to create a UDF several years back, intending to insert it manually into the ‘formula’ created for the chart and make the accumulation happen, but it was beyond my knowledge/abilities.
It would be nice to see a solution to this unfinished business, though I can’t remember why it was so important at the time!
Thanks for the interesting articles – I get them by RSS feed and email.
Mynda Treacy
Hi Stephen,
I’m not sure I follow what you want to do. Are you wanting the chart to have an automatic option to plot a cumulative value based on a series already in the chart? Perhaps you could share an example file so we can better understand what you’re after.
Cheers,
Mynda
Stephen
Mynda
Yes to the automatic option, but only the cumulative values need to be in the chart (versus date). A simple example where this is useful would be a record of miles travelled by car. Each row would have the miles for a single trip, with the date. The chart should plot the total mileage versus the date. I know it’s easy to create an extra helper column, but …
This could also be useful for time or money spent on a project.
I’ve a horrible feeling you’re going to tell me there’s a check box I’ve never noticed 😉
Regards
Stephen
Mynda Treacy
Hi Stephen,
Thanks for clarifying. I’d use a PivotTable to first summarise the data in your table into total by date, then insert a Pivot Chart to display the total miles per date.
Hope that helps.
Mynda
Stephen
Thanks Mynda. PivotTable, Power Pivot and PowerQuery are such powerful additions to Excel.
Thanks for all your help
Stephen