UDF Running for all Cells vs Only Corresponding Changed Cells

pocketrob

New Member
Joined
Nov 23, 2016
Messages
2
Greetings Excelers!

I'm running into an issue where all of my in-cell functions run whenever one is changed.
I've got a workbook that I use to pre-populate cells with the values of their corresponding activity/role. The values lives on another sheet.

I'm trying to make it so that the default values are held in excel memory, to avoid re-running them if they haven't changed, and to keep the workbook nimble, calculate only the corresponding functions to be triggered, not all of them.

I feel like I'm missing something simple...

Using Office 365 ProPlus, Windows 7 Professional

Example:
If the activity is "Plan", and the role is "Technical Consultant", it should populate the corresponding value of Plan/Technical Consultant from the "Roles" sheet into Cell B2 (10, in the example below).

AB
1Plan
2Technical Consultant10
3Business Analyst8

<tbody>
</tbody>

To do this, I've got a user defined function (UDF) called "GetWbsActivityTime" in the worksheet cell that passes the name of sTopLevel ("Plan") and the sActivity ("Technical Consultant").

<code>B2=GetWbsActivityTime(sTopLevel As String, sActivity As String)</code>
B2=GetWbsActivityTime(B1,A2)
B2=GetWbsActivityTime("Plan","Technical Consultant")

On the sheet "Role Defaults", I've defined a table of my defaults.

ABC
1PlanTest
2Technical Consultant1020
3Business Analyst812

<tbody>
</tbody>

<code>
Public Function GetWbsActivityTime(sTopLevel As String, sActivity As String) As Variant
Dim oDict As Object

Set oDict = GetWBS()​

GetWbsActivityTime = oDict(sTopLevel)(sActivity)​

End Function
</code>
GetWBS() checks to see if the Public oDictWbs object is populated

<code>
Public Function GetWBS() As Object
If Not oDictWbs Is Nothing Then
Set GetWBS = oDictWbs​
Exit Function
End If
Set GetWBS = RefreshWBS()​
End Function
</code>
The Function RefreshWBS builds the two-dimensional array of the "Role Defaults" table

<code>Public Function RefreshWBS()
Dim sDefault As Worksheet
Dim rTopLevels As Range
Dim rActivities As Range
Dim rIterator As Range
Dim rInnerIter As Range

Set oDictWbs = Nothing
'Both variables below establish the range that stores the fixed info (the default worksheet)
'Instead of hard coding in the range, create your own logic based on your needs and rules


Set sDefault = Sheets("Role Defaults")
Set rTopLevels = sDefault.Range("RoleDefaultsTable_Head")
Set rActivities = sDefault.Range("RoleDefaultsTable_FirstCol")

Set oDictWbs = CreateObject("Scripting.Dictionary")

For Each rIterator In rTopLevels
If Not oDictWbs.exists(rIterator.Value) Then​
Set oDictWbs(rIterator.Value) = CreateObject("Scripting.Dictionary")​
End If

For Each rInnerIter In rActivities
If Not oDictWbs(rIterator.Value).exists(rInnerIter.Value) Then​
oDictWbs(rIterator.Value)(rInnerIter.Value) = sDefault.Cells(rInnerIter.Row, rIterator.Column)​
End If
Next rInnerIter​
Next rIterator​

Set RefreshWBS = oDictWbs​

End Function


</code>
 

Excel Facts

What is the shortcut key for Format Selection?
Ctrl+1 (the number one) will open the Format dialog for whatever is selected.
Since oDict is local to GetWbsActivityTime, it is set to Nothing each time the function exits.

But, more important, you are trying to do what Microsoft has spend significant resources optimizing -- recomputing cells only when needed. I would let Excel's recalculation engine do the heavy lifting.

Just use a 2-factor (or a 2-D) lookup to get the values you want.
2D Lookup

Greetings Excelers!

I'm running into an issue where all of my in-cell functions run whenever one is changed.
I've got a workbook that I use to pre-populate cells with the values of their corresponding activity/role. The values lives on another sheet.

I'm trying to make it so that the default values are held in excel memory, to avoid re-running them if they haven't changed, and to keep the workbook nimble, calculate only the corresponding functions to be triggered, not all of them.

I feel like I'm missing something simple...

Using Office 365 ProPlus, Windows 7 Professional

Example:
If the activity is "Plan", and the role is "Technical Consultant", it should populate the corresponding value of Plan/Technical Consultant from the "Roles" sheet into Cell B2 (10, in the example below).

AB
1Plan
2Technical Consultant10
3Business Analyst8

<tbody>
</tbody>

To do this, I've got a user defined function (UDF) called "GetWbsActivityTime" in the worksheet cell that passes the name of sTopLevel ("Plan") and the sActivity ("Technical Consultant").

<code>B2=GetWbsActivityTime(sTopLevel As String, sActivity As String)</code>
B2=GetWbsActivityTime(B1,A2)
B2=GetWbsActivityTime("Plan","Technical Consultant")

On the sheet "Role Defaults", I've defined a table of my defaults.

ABC
1PlanTest
2Technical Consultant1020
3Business Analyst812

<tbody>
</tbody>

<code>
Public Function GetWbsActivityTime(sTopLevel As String, sActivity As String) As Variant
Dim oDict As Object

Set oDict = GetWBS()​

GetWbsActivityTime = oDict(sTopLevel)(sActivity)​

End Function
</code>
GetWBS() checks to see if the Public oDictWbs object is populated

<code>
Public Function GetWBS() As Object
If Not oDictWbs Is Nothing Then
Set GetWBS = oDictWbs​
Exit Function
End If
Set GetWBS = RefreshWBS()​
End Function
</code>
The Function RefreshWBS builds the two-dimensional array of the "Role Defaults" table

<code>Public Function RefreshWBS()
Dim sDefault As Worksheet
Dim rTopLevels As Range
Dim rActivities As Range
Dim rIterator As Range
Dim rInnerIter As Range

Set oDictWbs = Nothing
'Both variables below establish the range that stores the fixed info (the default worksheet)
'Instead of hard coding in the range, create your own logic based on your needs and rules


Set sDefault = Sheets("Role Defaults")
Set rTopLevels = sDefault.Range("RoleDefaultsTable_Head")
Set rActivities = sDefault.Range("RoleDefaultsTable_FirstCol")

Set oDictWbs = CreateObject("Scripting.Dictionary")

For Each rIterator In rTopLevels
If Not oDictWbs.exists(rIterator.Value) Then​
Set oDictWbs(rIterator.Value) = CreateObject("Scripting.Dictionary")​
End If

For Each rInnerIter In rActivities
If Not oDictWbs(rIterator.Value).exists(rInnerIter.Value) Then​
oDictWbs(rIterator.Value)(rInnerIter.Value) = sDefault.Cells(rInnerIter.Row, rIterator.Column)​
End If
Next rInnerIter​
Next rIterator​

Set RefreshWBS = oDictWbs​

End Function


</code>
 
Upvote 0
Thanks for the reply, tusharm. I've done the INDEX/MATCH 2D lookup, and found it takes a lot of resources when this is a 800 row x 55 column worksheet. I was hoping that putting the table in an array would speed up my calculations.
 
Upvote 0

Forum statistics

Threads
1,214,392
Messages
6,119,255
Members
448,879
Latest member
oksanana

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top