VBA Defining Multiple Named Ranges - excel

I am working with a sheet with almost 200 named ranges (each column is a NR). I now would like to make them dynamic i.e. instead of defining them like
PersonID = =RawData!$A$2:$A$100
I want to do it this way
PersonID = OFFSET(RawData!$A$2,0,0,COUNTA(RawData!$A:$A),1)
But I do not want to do this manually! Is there a way to do this in a texteditor outside Excel or is there a way to do this programatically? I already have the 200 NRs done in the first way in place, but the thought of manually go through them all to change is scaring me.

You can do it in VBA. Example to create a new name:
ActiveWorkbook.Names.Add Name:="PersonID", _
RefersTo:="=OFFSET(RawData!$A$2,0,0,COUNTA(RawData!$A:$A),1)"
To edit an already existing name:
ActiveWorkbook.Names("PersonID").RefersTo = _
"=OFFSET(RawData!$A$2,0,1,COUNTA(RawData!$A:$A),1)"
You indicate in a comment that you would also like to iterate through all named ranges to facilitate changing their definition. To loop through all names you can do this:
Dim nm As Name
For Each nm In ActiveWorkbook.Names
Debug.Print nm.Name
Next nm
or this:
Dim i As Long
For i = 1 To ActiveWorkbook.Names.Count
Debug.Print ActiveWorkbook.Names.Item(i).Name
Next i

This seems to be a pretty good tool to have in your toolbox?
Sub MakeRangesDynamic()
Dim i As Long
For i = 1 To ActiveWorkbook.Names.Count
If Not (ActiveWorkbook.Names.Item(i).Name = "NameToExclude1" Xor _
ActiveWorkbook.Names.Item(i).Name = "NameToExclude2" Xor _
ActiveWorkbook.Names.Item(i).Name = "NameToExclude3") Then
FindTheColumn = Mid$(ActiveWorkbook.Names.Item(i).RefersTo, 11, 2)
If Mid$(FindTheColumn, 2, 1) = "$" Then
FindTheColumn = Mid$(FindTheColumn, 1, 1)
Else
FindTheColumn = Mid$(FindTheColumn, 1, 2)
End If
DynNameString = "=OFFSET(RawData!$" & FindTheColumn & "$2,0,0,COUNTA(RawData!$" & FindTheColumn & ":$" & FindTheColumn & "),1)"
Debug.Print DynNameString
'ActiveWorkbook.Names.Item(i).Name.RefersTo = DynNameString
End If
Next i
End Sub
A special thanks goes to Jean-Francois for helping me out.
Change the RawData to your sheetname and the NameToExclude to your ranges to leave untouched.
Remove the last comment for making it happen! But be sure to make a backup copy first!!!!

Related

Creating a mixed reference named range with VBA

I'm trying to write some VBA that can create a named range if it matches a header. I've managed to achieve this but hit a snag that I can't seem to get to work.
I need the named range to be mixed reference only locking in the column. I've got this to work also but when trying to combine everything it doesn't carry the mixed ref.
Examples -
Sub test()
i = 1
Do Until Cells(1, i) = "Created Date"
i = i + 1
Loop
NR1 = Cells(1, i).Offset(1, 0).Address(False, True)
ActiveWorkbook.Names.Add Name:="Created_Date", RefersTo:=NR1
ActiveWorkbook.Names("Created_Date").Comment = ""
End Sub
The above will set the named range with the required mixed ref but obviously no sheet name -
so my thought was simply use activesheet.range(NR1) like so -
Sub test()
i = 1
Do Until Cells(1, i) = "Created Date"
i = i + 1
Loop
NR1 = Cells(1, i).Offset(1, 0).Address(False, True)
ActiveWorkbook.Names.Add Name:="Created_Date", RefersTo:=ActiveSheet.Range(NR1)
ActiveWorkbook.Names("Created_Date").Comment = ""
End Sub
This does create the named range on the sheet but the references go back to being locked to one cell!
any ideas?
You can use the External argument in the Range.Address property to make the Workbook and Sheet name part of the returned address.
If you put an address into the RefersTo argument, it just takes the string as its value instead of going to the cells or range that the address is pointing at. To fix this interaction, you can add an = in front of the address to make the String into a formula which Excel will evaluate.
So with the following changes, your code should do what you want:
NR1 = Cells(1, i).Offset(1, 0).Address(False, True, External:=True)
ActiveWorkbook.Names.Add Name:="Created_Date", RefersTo:="=" & NR1

Combine First and Last name into full name (VBA)

Long story short, I need pull a first name and last name into one string of text in VBA. This is a part of an automated report so I need it to loop until the next cell is empty.
I can for some reason split a full name into separate text, but combining the two cells is not working for me.
Dim first As Variant, last As Variant, full As string
With Worksheets("RG0054_Term Validation")
.Columns("D").Insert
.Range("D10").Value = "Employee Name"
Set first = .Range("B11")
Set last = .Range("C11")
full = 0
Do Until IsEmpty(first.Value)
Do Until IsEmpty(last.Value)
ActiveCell.Offset(0, full).Formula = first.Value & " " & last.Value
full = full + 1
Set last = last.Offset(0, 1)
Loop
Set last = .Range("C11")
Set title = title.Offset(1, 0)
Loop
End With
End Sub
turn b11 & c11 = d11 for i to 3000
I am guessing I could do a loop first = i to 3000 and last = 1 to 2 .. but I am not sure how to organize this... Also an explanation of the script logic would be great so I can learn the code syntax rather than regurgitate it.
No need for a loop. The simplest way would be to find the last row in Col B and then enter the formula in Col D in one go. For example
With Worksheets("RG0054_Term Validation")
Lrow = .Range("B" & .Rows.Count).End(xlUp).Row
.Columns(4).Insert
.Range("D10").Value = "Employee Name"
.Range("D11:D" & Lrow).Formula = "=B11 & "" "" & C11"
End With

Excel VBA macro reading one column with differing text

I was tasked with creating a code that will check to see if internal hyperlinks in an excel spreadsheet worked. This code first changes the formulas that were on the spreadsheet and makes them actual hyperlinks (they were originally formulas linking the locations together). The problem that I have now is that I want to create hyperlinks ONLY if Column S has text. If it doesn't, I don't want the "E-COPY" text to be displayed. All of the text in Column S varies (not one line has the same characters), which is why I'm drawing a blank is to how I tell the program to only continue if it has any text, not anything specific. I am working with Excel 2016.
Also, I am doing this to 71935 and counting rows; is there a limit to how many it can go through? If so, what can I do about it?
Thank you!
Sub CreateHyperlinks()
Dim FN As Variant
Dim Path As Variant
Dim count As Variant
Sheets(1).Activate
count = WorksheetFunction.CountA(Sheets(1).Range("A:A"))
For i = 2 To count
If Range("AM" & i).Value = "Yes" And Columns("S") = Then
Range("E" & i).Value = ""
Path = Sheets(1).Range("R" & i).Value
FN = Sheets(1).Range("S" & i).Value
Sheets(1).Range("E" & i).Select
Selection.ClearFormats
Selection.Hyperlinks.Add Anchor:=Selection, Address:=Path & FN, TextToDisplay:="E-COPY"
Range("AM" & i).Value = " "
End If
Next i
End Sub
If you just need to check for any content in ColS then:
If Range("AM" & i).Value = "Yes" And Len(Range("S" & i).Value) > 0 Then
Few things:
'make a reference to the sheet you're working with
Dim ws As Worksheet
Dim wb As Workbook
Set wb = Excel.Application.ThisWorkbook
Set ws = wb.Worksheets(1)
'gets the absolute last row with data in it // ignores empty cells
count = ws.UsedRange.Rows.Count
personally, i hate working with named ranges, so i would suggest setting range references like so
what you wrote
Path = Sheets(1).Range("R" & i).Value
what i believe it should look like
Path = ws.Cells(i, 18).Value
if you want to test the type when working with variants, try this:
'tests the type associated with the variant. an 8 = string
If VarType(ws.Cells(i, 19).Value) = 8 Then
'do your thing
'tests if the value is null
ElseIf VarType(ws.Cells(i, 19).Value) = 0 Then
'do your other thing
here's a list of the vartype enumeration to help you out.
hope it helps!

Read Wildcard As Asterisk

I had a piece of code commissioned earlier this week (cheaper to have an expert write it than for me to spend a week trying to!). However, when putting it use I've hit a bit of a snag.
The macro looks at a name on one excel worksheet, matches it to a list of names and associated ID numbers on a different worksheet, then inserts the ID on the first worksheet. This was all fine until I started using it on real data.
Here's some sample data (all of this information is in one cell...):
WARHOL*, Andy
PETO, John F
D3 GRECO, Emilio -20th C
HASELTINE, William Stanley
D3 DALI, Salvador
D3 SOSNO, Sacha
WEGMAN**, WILLIAM
One asterisk means it's a print, two a photograph, D3 a sculpture, and nothing a painting.
When I run the code with this data, it sees * as a wildcard, and so will always insert the ID of the first variation of the artist in the sheet. What I need is a way for the macro to not read it as a wildcard.
I did some research, and found that inserting ~ before * negates the wildcard properties. How would I make my code do this? I've discovered the main issue of having code written by someone else... You might not understand it!
Here is the code:
Public Sub match_data()
'ctrl+r
On Error GoTo errh
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim r1, r2, i, exc As Long
Dim fp As Range
Sheets("Data").Activate
r1 = Cells(Rows.Count, "B").End(xlUp).Row
r2 = Sheets("List").Cells(Sheets("List").Rows.Count, "B").End(xlUp).Row
'MsgBox r1 & r2
exc = 0
For i = 2 To r1
If Range("B" & i).Value <> "" Then
With Sheets("List").Range("B2:B" & r2)
Set fp = .Find(Range("B" & i).Value, LookIn:=xlValues, lookat:=xlWhole)
If Not fp Is Nothing Then
Range("B" & i).Interior.Color = xlNone
Range("A" & i).Value = Sheets("List").Range("A" & fp.Row).Value
Else
Range("B" & i).Interior.Color = xlNone
Range("B" & i).Interior.Color = vbYellow
exc = exc + 1
End If
End With
End If
Next i
MsgBox "There are " & exc & " exceptions."
errh:
If Err.Number > 0 Then
MsgBox Err.Description
End If
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Oh also, I would need to do this for the list of names and ID's wouldn't I? If so, that only needs doing once, so if you could give me a hint about that too, I'd be so grateful!
Thanks!
PS I know the system we are using at the moment absolutely sucks (definitely not 3rd form normalised!), but we are fast running out of time and money, and need to get our product up and running ASAP!
EDIT: To clarify, here is a pic of the spreadsheets I'm working with... Obviously in cells A14 and A15 I wanted the ID numbers 11 & 12 respectively
Here is one way to tell the stars from the planets:
Sub StaryNight()
Dim s As String, OneStar As String, TwoStar As String, ThreeStar As String
OneStar = "*"
TwoStar = "**"
ThreeStar = "***"
t = Range("A1").Text
ary = Split(t, ",")
s = ary(0)
If Right(s, 3) = ThreeStar Then
MsgBox "scupture"
Exit Sub
ElseIf Right(s, 2) = TwoStar Then
MsgBox "photograph"
Exit Sub
ElseIf Right(s, 1) = OneStar Then
MsgBox "print"
Exit Sub
End If
MsgBox "Painting"
End Sub
Okay, I have solved the problem! I had a play around with changing the variables in the Find and Replace box.
If I put ~* in both the find AND replace boxes, and uncheck Match entire cell contents, I can replace all of the * with ~* (really don't understand that but oh well!)
So I do this on the Data worksheet, but NOT on the List worksheet, run the macro as normal and the problem is solved!

Write shorter For Loop for Array

I wrote the following For Loop to copy and paste several ranges from one sheet to another. I used an array. The procedure works fine, but I was looking for a shorter way to do the same.
Dim copyRange(1 To 3) As String
Dim pasteRange(1 To 3) As String
Dim refRange(1 To 3) As String
Dim i As Long
copyRange(1) = "A5"
copyRange(2) = "G5"
copyRange(3) = "H5"
refRange(1) = "A"
refRange(2) = "G"
refRange(3) = "H"
pasteRange(1) = "BE3"
pasteRange(2) = "CA2"
pasteRange(3) = "CD2"
For i = 1 To 3
refApplicantFlow.Range(copyRange(i), refApplicantFlow.Range(refRange(i) & Rows.Count).End(xlUp)).Copy _
calcCalculations.Range(pasteRange(i))
Next i
First of all, I would strongly recommend, to store the references to the cells as hard coded strings in your macro. The moment your workbook's structure is slightly adjusted, your macros will fail/copy the wrong things!
Instead, use named ranges. I.e. click on A5 and assign the name Source_1 or so to it. G5 will become Source_2, H5 Source_1, G5 Target_1, etc.
The use this code:
lngRows = Rows.Count
For i = 1 To 3
Range("Target_"&i).Resize(lngRows).Value = Range("Source_"&i).Resize(lngRows).Value
Next
This way, your macro will still work, even if the workbook structure changes! And your line of code became shorter! ;-)
Your macro is pretty efficient if the intent was to include a loop and arrays. The same thing could be accomplished in 3 lines:
refApplicantFlow.Range("A5", refApplicantFlow.Range("A" & Rows.Count).End(xlUp)).Copy _
calcCalculations.Range("BE3")
refApplicantFlow.Range("G5", refApplicantFlow.Range("G" & Rows.Count).End(xlUp)).Copy _
calcCalculations.Range("CA2")
refApplicantFlow.Range("H5", refApplicantFlow.Range("H" & Rows.Count).End(xlUp)).Copy _
calcCalculations.Range("CD2")

Resources