Trying to Replace Named Ranges with Cell References

The_Accountant

New Member
Joined
Sep 23, 2021
Messages
5
Office Version
  1. 2019
Platform
  1. Windows
I have a workbook with 25-30 worksheets and every single cell that would have a cell reference uses a named range instead. I am trying to replace them all with cell references using the following macro (found on google):

Sub AbsoleteNamesWithRelativeRefs()
Dim Rng As Range
Dim WorkRng As Range
Dim xName As Name
On Error Resume Next
xTitleId = "Input_Range"
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address, Type:=8)
Set WorkRng = WorkRng.SpecialCells(xlCellTypeFormulas)
For Each Rng In WorkRng
For Each xName In ThisWorkbook.Names
If InStr(Rng.Formula, xName.Name) > 0 Then
Rng.Formula = VBA.Replace(Rng.Formula, xName.Name, VBA.Replace(VBA.Replace(xName.RefersTo, "=", ""), "$", ""))
End If
Next
Next
End Sub

The named ranges are based on dates - some examples are:
=_12stdjan1
=_12stdjan14
=_12stdjan15

For any ranges that have a two digit day (e.g. 2nd and 3rd example above), the macro is only recognizing the name through the tens place and leaving the final digit at the end of the resulting cell reference. So it pulls the cell reference for the Jan 1 date and makes the resulting cell reference have an extra digit.

For example, if the name "_12stdjan14" refers to cell $A$15 and the name "_12stdjan1" refers to cell $B$15, running the macro for "_12stdjan14" would result in a cell reference of $B$154.

Is there any way to fix this issue? I am unfortunately very inexperienced with VBA if that isn't already apparent.
 

Excel Facts

Lock one reference in a formula
Need 1 part of a formula to always point to the same range? use $ signs: $V$2:$Z$99 will always point to V2:Z99, even after copying
Replacing named ranges with absolute cell references is equivalent to deleting the named ranges: (Make a copy of the Excel Workbook before running!)
VBA Code:
Sub DeleteAllNamedRanges()
On Error Resume Next 'For Excel named ranges that start with "_".
Dim MyName As name
For Each MyName In Names
    ThisWorkbook.Names(MyName.name).Delete
Next
End Sub
 
Upvote 0
Ah that's exactly what I am looking to do! I will give this a shot. Thank you for the help and quick response!
 
Upvote 0
Ah that's exactly what I am looking to do! I will give this a shot. Thank you for the help and quick response!
You're very welcome.

And there's ways to delete them from a specific sheet that you specific too. (Just run the Test sub if interested.)
VBA Code:
Sub Test__Delete_ALL_CellReferenceNamesFromSheetNamed()
Call Delete_ALL_CellReferenceNamesFromSheetNamed("Sheet1") 'Replace "Sheet1" with the specific sheet name that you want to delete all of its named ranges.
End Sub

Sub Delete_ALL_CellReferenceNamesFromSheetNamed(sheetName As String)
Dim n As name
For Each n In ThisWorkbook.Names
    If InStr(n.RefersTo, sheetName) > 0 Then n.Delete
Next n
End Sub

Even more handy, here's how to delete all of the hideous #REF! errors!
VBA Code:
Sub Delete_All_Named_Ranges_With_REF_Error()
Dim n As name
For Each n In ThisWorkbook.Names
    If InStr(n.RefersTo, "#REF!") > 0 Then n.Delete
Next n
End Sub
 
Upvote 0
Replacing named ranges with absolute cell references is equivalent to deleting the named ranges: (Make a copy of the Excel Workbook before running!)
VBA Code:
Sub DeleteAllNamedRanges()
On Error Resume Next 'For Excel named ranges that start with "_".
Dim MyName As name
For Each MyName In Names
    ThisWorkbook.Names(MyName.name).Delete
Next
End Sub
Looks like this is only deleting the named range but doesn't replace with the corresponding cell reference (results in #NAME error). Same result as if I go into Name Manager and delete all.
 
Upvote 0
Looks like this is only deleting the named range but doesn't replace with the corresponding cell reference (results in #NAME error). Same result as if I go into Name Manager and delete all.
I'm working on a completely different (potential) solution now. (I guess it was worth a try with the easy route first!)
 
Upvote 0
Looks like this is only deleting the named range but doesn't replace with the corresponding cell reference (results in #NAME error). Same result as if I go into Name Manager and delete all.

I finally got to doing this! (This code should work for all cases.) This code will do replacements in ALL cells (in all sheets) in the Workbook which have at least one named range cell reference in them.

The basic idea turned out to be (and I looked into Excel's Range.DirectPrecedents!) just to (1) Put all named ranges into an array and sort by length. (2) Substitute the longest named range names first. And to substitute, use a regular expression. From Excel Operators, I got an exhaustive list of all operators that can appear in formulas. (If I missed any, please someone point it out. It's very easy to modify the
VBA Code:
"[&%^*/+-=>< ,:)( ]"
portion of this code! (Just literally insert additional characters (if there is any more characters that can be adjacent to cell references in formulas) between the two [ ].)

If anyone wants me to make a video explaining any of this further (or just in writing), let me know.

If you want to just test that it will do the proper substitutions without actually doing them
  1. Comment the line of code identified with ***.
  2. Uncomment the 6 Debug.Print lines above that line.

Lastly, it's a more code than it needs to be simply because I have a handful of small subs which do simple tasks (and I have a test sub for each of those functions). I could put it all in one sub (and shorten the names of the functions and variables), but that's poor practice IMO.

VBA Code:
Option Explicit

Sub AbsoleteNamesWithRelativeRefs()

'We're going to be changing a lot of formulas.  Have the sheet delate updating the formulas until after we're done.
Application.Calculation = xlCalculationManual

Dim tempFormula As String
Dim refersTo_replacement As String
Dim i As Integer
Dim xName As Name
Dim rng As Range

Dim sht As Worksheet
For Each sht In ThisWorkbook.Sheets
    If This_Sheet_Has_At_Least_One_Formula_In_It(sht.Name) = False Then GoTo Next_Sheet
    For Each rng In sht.Cells.SpecialCells(xlCellTypeFormulas)
 
        ReDim arrayOfNamedRangesInCurrentCellFormula(0 To 0) As String
        For Each xName In ThisWorkbook.Names
            If InStr(rng.Formula, xName.Name) > 0 Then arrayOfNamedRangesInCurrentCellFormula = Append_StringType(arrayOfNamedRangesInCurrentCellFormula, xName.Name)
        Next xName

        'If the current rng doesn't have any named range cell references (just absolute), skip the remaining steps.
        If UBound(arrayOfNamedRangesInCurrentCellFormula) = 0 Then GoTo Next_Range

        'Sort the named ranges referenced in this rng by length in ascending order with Quicksort__Of_StringArray_By_Length.
        arrayOfNamedRangesInCurrentCellFormula = Quicksort__Of_StringArray_By_Length(arrayOfNamedRangesInCurrentCellFormula, 0, UBound(arrayOfNamedRangesInCurrentCellFormula))

        'Call MA(arrayOfNamedRangesInCurrentCellFormula) 'Uncomment to view the output from the Sort for the very first rng.  (It will quit execution after that.)

        tempFormula = rng.Formula
    
        'We want to substitute larger named range names FIRST, so we DECREMENT (because we SortA previously).
        For i = UBound(arrayOfNamedRangesInCurrentCellFormula) To 1 Step -1
            refersTo_replacement = Replace(Replace(ThisWorkbook.Names.Item(arrayOfNamedRangesInCurrentCellFormula(i)).RefersTo, "=", ""), "$", "")
            tempFormula = Replace_NamedRanges(tempFormula, "[&%^*/+-=>< ,:)( ]" & arrayOfNamedRangesInCurrentCellFormula(i) & "[&%^*/+-=>< ,:)( ]", arrayOfNamedRangesInCurrentCellFormula(i), refersTo_replacement)
        Next i

        '(Uncomment these to lines to view examples in the immediate (Ctrl+G) window.
        'Debug.Print "--------"
        'Debug.Print "From range " & rng.Address & " in sheet " & rng.Parent.Name & ","
        'Debug.Print tempFormula
        'Debug.Print ""
        'Debug.Print "Original formula: " & rng.Formula
        'Debug.Print ""
    
        rng.Formula = tempFormula '***
Next_Range:
    Next rng
Next_Sheet:
Next sht

'Have Excel now calculate the change in all formulas.
Application.Calculation = xlCalculationAutomatic

End Sub


Sub Test__All_Start_Locations_Of_Named_Range()

'A regular expression.  What's in between the [] is all characters that we are looking to see is to the left
'and right of our named range's names as we search for them (and replace them) in the following function.
Dim isAnOperatorInExcelFormulas As String
isAnOperatorInExcelFormulas = "[&%^*/+-=>< ,:)( ]"

'We can have a named range named "a".
Dim namedRangesName As String
namedRangesName = "a"

'Let's really test this out by putting "a" at the end of the formula, as well as have "aa" and "concAtenAte" in the formula.
Dim excelFormula As String
excelFormula = "=d*e+a-f+aa*b+CONCATENATE(d,f)+a"

MsgBox Replace_NamedRanges(excelFormula, isAnOperatorInExcelFormulas & namedRangesName & isAnOperatorInExcelFormulas, namedRangesName, "=Sheet1!A1")

End Sub
Function Replace_NamedRanges(strValue As String, strPattern As String, namedRangesName As String, replacement As String)
'https://stackoverflow.com/questions/8301622/excel-vba-regex-match-position
'This assumes that lowercase "|" is NOT in the strValue anywhere.  Change to another character if this is not true!

strValue = strValue & "*" 'just in case the last character in the formula is the end of a named range.

Dim location As Integer

With CreateObject("VBScript.RegExp")
    .Pattern = strPattern
    .IgnoreCase = False

Look_For_More_Occurrences:
    If .test(strValue) = True Then
        '+ 1 because it's a string with starting index of 1 instead of 0.
        'And another + 1 because without it, that's where the operator is, not where the named range's first character starts.
        location = .Execute(strValue)(0).firstindex + 1 + 1
        strValue = SubString(strValue, 1, location - 1) & "|" & SubString(strValue, location + Len(namedRangesName), Len(strValue))
        GoTo Look_For_More_Occurrences
    End If
End With

strValue = Replace(strValue, "|", replacement)
Replace_NamedRanges = SubString(strValue, 1, Len(strValue) - 1) 'omit the last character *.

End Function


Function Quicksort__Of_StringArray_By_Length(vArray As Variant, arrLbound As Long, arrUbound As Long)
'Modified to sort by argument length instead of argument value.
'Original code from:  https://wellsr.com/vba/2018/excel/vba-quicksort-macro-to-sort-arrays-fast/
'Sorts a one-dimensional VBA array from smallest to largest
'using a very fast quicksort algorithm variant.
Dim pivotVal As Variant
Dim vSwap    As Variant
Dim tmpLow   As Long
Dim tmpHi    As Long

tmpLow = arrLbound
tmpHi = arrUbound
pivotVal = vArray((arrLbound + arrUbound) \ 2)

Do While (tmpLow <= tmpHi) 'divide
   Do While (Len(vArray(tmpLow)) < pivotVal And tmpLow < arrUbound)
      tmpLow = tmpLow + 1
   Loop

   Do While (pivotVal < Len(vArray(tmpHi)) And tmpHi > arrLbound)
      tmpHi = tmpHi - 1
   Loop

   If (tmpLow <= tmpHi) Then
      vSwap = vArray(tmpLow)
      vArray(tmpLow) = vArray(tmpHi)
      vArray(tmpHi) = vSwap
      tmpLow = tmpLow + 1
      tmpHi = tmpHi - 1
   End If
Loop

  If (arrLbound < tmpHi) Then Quicksort__Of_StringArray_By_Length vArray, arrLbound, tmpHi 'conquer
  If (tmpLow < arrUbound) Then Quicksort__Of_StringArray_By_Length vArray, tmpLow, arrUbound 'conquer

Quicksort__Of_StringArray_By_Length = vArray

End Function


Sub Test__This_Sheet_Has_At_Least_One_Formula_In_It()
MsgBox This_Sheet_Has_At_Least_One_Formula_In_It(ActiveSheet.Name)
End Sub
Function This_Sheet_Has_At_Least_One_Formula_In_It(sheetName As String)

This_Sheet_Has_At_Least_One_Formula_In_It = True

On Error GoTo Has_No_Formulas
Dim rng As Range
Set rng = Sheets(sheetName).Cells.SpecialCells(xlCellTypeFormulas)

Exit Function
Has_No_Formulas:
This_Sheet_Has_At_Least_One_Formula_In_It = False

End Function


Sub Test__Append_StringType()

ReDim sampleArray(1 To 2) As String
sampleArray(1) = "item 1"
sampleArray(2) = "item 2"

sampleArray = Append_StringType(sampleArray, "##Address_1 Line 1##")

Call MA(sampleArray)

End Sub
Function Append_StringType(arr As Variant, arg As Variant)
'Two possible errors from client subs:
'(1) arr is not of type variant.
'(2) arr is defined as Dim array() as Variant instead of ReDim array(1 to x) as variant.

    Dim lowerBOundOfInputArray As Integer
    lowerBOundOfInputArray = LBound(arr)

    Dim upperBoundOfInputArray As Integer
    upperBoundOfInputArray = UBound(arr)

    ReDim newArray(lowerBOundOfInputArray To upperBoundOfInputArray) As String
    newArray = arr
 
    ReDim Preserve newArray(lowerBOundOfInputArray To upperBoundOfInputArray + 1)
    newArray(upperBoundOfInputArray + 1) = arg
 
    Append_StringType = newArray

End Function


Sub Test__SubString()
MsgBox SubString("ABCDEF", 3, 5)
End Sub
Function SubString(inputString As String, Start As Integer, Finish As Integer)
On Error GoTo Quit
SubString = Mid(inputString, Start, Finish - Start + 1)
Quit:
End Function


Sub Test__MA()
ReDim test_list(0 To 4) As Variant
test_list(0) = 45
test_list(1) = "A"
test_list(2) = 1
test_list(3) = "C"
test_list(4) = 3.14
Call MA(test_list)

End Sub
Sub MA(args As Variant)

On Error Resume Next 'In case the array to be displayed doesn't have a 0th argument.
Dim i As Integer
i = 0
Do While i <= Length(args)
    MsgBox args(i)
    i = i + 1
Loop
End 'Quit execution of all subs.
End Sub


Sub Test__Length()
ReDim test_list(1 To 5) As Variant
test_list(1) = "A"
test_list(2) = 1
test_list(3) = "C"
test_list(4) = 3.14
test_list(5) = 45
MsgBox Length(test_list)
End Sub
Function Length(args As Variant)
'Gives the length of an array (number of arguments).
Length = UBound(args) - LBound(args) + 1
End Function
 
Last edited:
Upvote 0
Also, I think it makes much more sense that if the named range was in the sheet of focus, that the formulas in that (same) sheet which reference it should not have SheetName! in front of the range address.

You can simply insert the following IF statement that's center line of this loop to "fix" that.
VBA Code:
        For i = UBound(arrayOfNamedRangesInCurrentCellFormula) To 1 Step -1
            refersTo_replacement = Replace(Replace(ThisWorkbook.Names.Item(arrayOfNamedRangesInCurrentCellFormula(i)).RefersTo, "=", ""), "$", "")
            If SubString(refersTo_replacement, 1, InStr(refersTo_replacement, "!") - 1) = sht.Name Then refersTo_replacement = SubString(refersTo_replacement, InStr(refersTo_replacement, "!") + 1, Len(refersTo_replacement))
            tempFormula = Replace_NamedRanges(tempFormula, "[&%^*/+-=>< ,:)(]" & arrayOfNamedRangesInCurrentCellFormula(i) & "[&%^*/+-=>< ,:)(]", arrayOfNamedRangesInCurrentCellFormula(i), refersTo_replacement)
        Next i

And named ranges are technically absolute cell references $A$1, so it may also make sense (if you want to keep the "functionality" of the sheet, to omit the Range.Replace() $ with "")
VBA Code:
        For i = UBound(arrayOfNamedRangesInCurrentCellFormula) To 1 Step -1
            refersTo_replacement = Replace(ThisWorkbook.Names.Item(arrayOfNamedRangesInCurrentCellFormula(i)).RefersTo, "=", "")
            If SubString(refersTo_replacement, 1, InStr(refersTo_replacement, "!") - 1) = sht.Name Then refersTo_replacement = SubString(refersTo_replacement, InStr(refersTo_replacement, "!") + 1, Len(refersTo_replacement))
            tempFormula = Replace_NamedRanges(tempFormula, "[&%^*/+-=>< ,:)(]" & arrayOfNamedRangesInCurrentCellFormula(i) & "[&%^*/+-=>< ,:)(]", arrayOfNamedRangesInCurrentCellFormula(i), refersTo_replacement)
        Next i

But another regular expression substitution would need to be done to make just the cell references that were previously named ranges (as we can have a mixture) as absolute. Maybe I can get to this later. Just let me know.
 
Upvote 0
For any ranges that have a two digit day (e.g. 2nd and 3rd example above), the macro is only recognizing the name through the tens place and leaving the final digit at the end of the resulting cell reference. So it pulls the cell reference for the Jan 1 date and makes the resulting cell reference have an extra digit.

For example, if the name "_12stdjan14" refers to cell $A$15 and the name "_12stdjan1" refers to cell $B$15, running the macro for "_12stdjan14" would result in a cell reference of $B$154.

Is there any way to fix this issue? I am unfortunately very inexperienced with VBA if that isn't already apparent.
The simplest fix would be to rename the Names where the day number is a single digit to a two-digit (leading zero) name. So, if you changed the name _12stdjan1 to _12stdjan01 then all of your "date names" would be unique and the problem you are having with your code would disappear.
 
Upvote 0
The simplest fix would be to rename the Names where the day number is a single digit to a two-digit (leading zero) name. So, if you changed the name _12stdjan1 to _12stdjan01 then all of your "date names" would be unique and the problem you are having with your code would disappear.
Just as a follow up, if the ONLY Defined Names that start with an underline character and end with a digit refer to these particular Names that you are having trouble with in the code you posted, then you can use this code to insert the 0 before the last digit when there is only a single digit at the end...
VBA Code:
Sub MakeSingleDigitDayNumberToTwoDigits()
  Dim N As Name
  For Each N In Names
    If N.Name Like "_*[!0-9]#" Then N.Name = Application.Replace(N.Name, Len(N.Name), 0, "0")
  Next
End Sub
Note: This code will process look at every Defined Name in the workbook, not just the Names on the active sheet.
 
Last edited:
Upvote 0

Forum statistics

Threads
1,224,820
Messages
6,181,159
Members
453,021
Latest member
Justyna P

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