My goal is to name my just-pasted range something unique to I can find it in the future.
The copied and pasted range comes from a drop-down menu, and thus must be modified
Selection.Name.Formula = "=""AddSection_""&SUBSTITUTE('Add Section'!D3,"" "","""")"
If they select Oil Furnace in D3's drop down, then that section is copied and pasted. It should be named "AddSection_OilFurnace"
Is this possible?
What I would REALLY love is if I could have a named range that updates based on how many exist before it. For example, the above would be "AddSection_OilFurnace1" and the next section would be "AddSection_GasFurnace2" and so on. But I have no idea how or if that is possible haha. Would it be something like:
Worksheets("Add Section").ranges.count
Is that possible and how would it go into my naming formula?
I'm super new to VBA so thank you for any and all help!
I think YowE3K has the right approach. I refactored his code because I don't like Do Loop.
Sub AddName()
Dim myNameBase As String
Dim arr() As String
Dim maxName As Long
Dim n As Name
myNameBase = "AddSection_" & Replace(Worksheets("Add Section").Range("D3").Value, " ", "")
For Each n In Names
If n.Name Like myNameBase & "*" Then
If n.Name = myNameBase Then
maxName = 1
ElseIf n.Name Like myNameBase & ".*." Then
arr = Split(n.Name, ".")
If arr(UBound(arr) - 1) >= maxName Then maxName = arr(UBound(arr) - 1) + 1
End If
End If
Next
Selection.Name = myNameBase & IIf(maxName, "." & maxName & ".", "")
End Sub
YowE3K Thanks for the help!
I believe what you are trying to do is:
Selection.Name = "AddSection_" & Replace(Worksheets("Add Section").Range("D3").Value, " ", "")
or, setting it up to ensure that the range name has not yet been used, perhaps something like:
Dim myName As String
Dim maxSuffix As Long
Dim n As Name
myName = "AddSection_" & Replace(Worksheets("Add Section").Range("D3").Value, " ", "")
maxSuffix = 0
For Each n In Names
If Left(n.Name, Len(myName)) = myName Then
If IsNumeric(Mid(n.Name, Len(myName) + 1)) Then
If CLng(Mid(n.Name, Len(myName) + 1)) > maxSuffix Then
maxSuffix = CLng(Mid(n.Name, Len(myName) + 1))
End If
End If
End If
Next
Selection.Name = myName & (maxSuffix + 1)
This only increments the count if the existing base name has been used before, i.e. AddSection_OilFurnace1, then AddSection_OilFurnace2, then maybe AddSection_GasFurnace1 - it doesn't go AddSection_OilFurnace1, AddSection_GasFurnace2, AddSection_OilFurnace3 - but maybe it is useful.
Related
I would like to rename my spreadsheet based on specific cell value.
For example my cell A1 for the different spreadsheet are as follow:
Spreadsheet 1: Invoice 120894, Att.:LVM & TPM , ATM Username
Spreadsheet 2: Invoice 120896, Att: TAM TAM, ATM Username
I'd like to give my spreadsheet the name in cell A1 with for instance LVM & TPM and TAM TAM.
Could someone please help?
I have found a code which would help renaming but I am not sure how to ignore the values and special characters around :
Sub RenameSheet()
'VARIABLES DECLARATION
Dim rs As Worksheet
Dim new_name As String, tmp_new_name As String
Dim counter As Integer: counter = 0
Dim counter1 As Integer: counter1 = 1
Dim allNames As Object
'CODE
Set allNames = CreateObject("Scripting.Dictionary")
For Each rs In Sheets
'FIRST, LET'S PARSE THE NAME "LAST NAME" + ", " + "NAME INITIAL" + "."
new_name = Split(rs.Range("A1"), " ")(1)
'CHECK IF IT EXISTS
If allNames.Exists(new_name) Then
'ADD A COUNTER "(N)" UNTIL IT DOESN'T EXIST ANYMORE
tmp_new_name = new_name
Do While allNames.Exists(tmp_new_name) <> False
tmp_new_name = new_name & " (" & counter1 & ")"
counter1 = counter1 + 1
Loop
counter1 = 1
new_name = tmp_new_name
End If
'RENAME
rs.Name = new_name
counter = counter + 1
'KEEP THE NAME STORED IN MEMORY (INTO THE DICTIONARY)
allNames.Add rs.Name, counter
Next rs
End Sub
Thank you !
Here is a good example. With string Hack&Slash you always have to keep in mind the formatting of the strings, and sometimes have to play around with it to get it just right.
Private Sub this()
Dim this$: this = "Invoice 120894, Att.:LVM & TPM , ATM Username"
this = Mid(this, InStr(1, this, ":") + 1, Len(this) - InStrRev(this, ",") - 3)
Debug.Print ; this
End Sub
b/c i get the feeling you're super new to this, I figure id expand on my example so that you can make sense of what it is doing
Private Sub this()
Dim this$: this = "Invoice 120894, Att.:LVM & TPM , ATM Username"
' 123456789012314567890123456789012345678901234
this = Trim(this)
'String to use starting point Length of extracted string
this = Mid(this, InStr(1, this, ":") + 1, Len(this) - InStrRev(this, ",") - 3)
' start at position 21 to position 29
Debug.Print ; this
End Sub
See if this helps - you need to be able to tell the compiler where to find the value you want to manipulate.
Private Sub this()
Dim this$
this = ThisWorkbook.worksheets("yourworksheetname").Range("A1").Value
this = Mid(this, InStr(1, this, ":") + 1, Len(this) - InStrRev(this, ",") - 3)
Debug.Print ; this
End Sub
Here's a quick sub routine to extract the details you need from the strings you start with, providing they're always in the same format, I'm sorry I can't elaborate more at this time since I'm leaving work, I hope you can apply it to your code.
Sub f()
Dim x As String
Dim n As String
x = "Invoice 120894, Att.:LVM & TPM , ATM Username"
n = Left(x, Len(x) - InStr(1, x, ","))
n = Trim(Right(n, Len(n) - InStr(1, n, ":")))
MsgBox n
End Sub
Ok so I have two columns of data as follows
Personalisation Max Char | Personaisation Field
1x15x25 | Initial, Name, Date
Previously I was using the following vba function (As excel16 has no TEXTJOIN)
Function TEXTJOIN(delim As String, skipblank As Boolean, arr)
Dim d As Long
Dim c As Long
Dim arr2()
Dim t As Long, y As Long
t = -1
y = -1
If TypeName(arr) = "Range" Then
arr2 = arr.Value
Else
arr2 = arr
End If
On Error Resume Next
t = UBound(arr2, 2)
y = UBound(arr2, 1)
On Error GoTo 0
If t >= 0 And y >= 0 Then
For c = LBound(arr2, 1) To UBound(arr2, 1)
For d = LBound(arr2, 1) To UBound(arr2, 2)
If arr2(c, d) <> "" Or Not skipblank Then
TEXTJOIN = TEXTJOIN & arr2(c, d) & delim
End If
Next d
Next c
Else
For c = LBound(arr2) To UBound(arr2)
If arr2(c) <> "" Or Not skipblank Then
TEXTJOIN = TEXTJOIN & arr2(c) & delim
End If
Next c
End If
TEXTJOIN = Left(TEXTJOIN, Len(TEXTJOIN) - Len(delim))
End Function
This would change 1x15x25 into 1-1, 2-15, 3-25using the following formula
{=TEXTJOIN(", ",TRUE,ROW(INDIRECT("1:" & LEN(A1)-LEN(SUBSTITUTE(A1,"x",""))+1)) & " - " & TRIM(MID(SUBSTITUTE(A1,"x",REPT(" ",999)),(ROW(INDIRECT("1:" & LEN(A1)-LEN(SUBSTITUTE(A1,"x",""))+1)) -1)*999+1,999)))}
Due to the fact, my original method was not specific enough I've been forced to go back to the drawing board.
From the Above, I am wanting to produce the following.
1-2-Initial, 2-15-Name, 3-25-Date
I am a developer but not in visual basic and the worst part Is I know what I would do with a database and PHP just don't have enough knowledge to transfer that to excel.
So I need to either by formula or function
Take 2 Columns and split by a delimiter
Then count the entries on each (Maybe only one)
Then for each in the range create a new string adding the count-col1-col2
I cannot change the data as its given by the supplier
I have a basic understanding of VBA so explain don't belittle
UPDATED (DATA SNAPSHOTS)
This Example uses the formula above a little-jazzed up.
As you can see each row starts the count again Ignore the Personalization/Message line parts I can add these again later
I am in a mega rush so only whipped this up with one row of values (in A1 and B1)
I hope you can step through to understand it, wrap it in another loop to go through your 6000 rows, and change the msgbox to whatever output area you need... 6000 rows should be super quick:
Sub go()
Dim a() As String
Dim b() As String
Dim i As Long
Dim str As String
' split A1 and B1 based on their delimiter, into an array a() and b()
a() = Split(Range("A1").Value2, "x")
b() = Split(Range("B1").Value2, ",")
' quick check to make sure arrays are same size!
If UBound(a) <> UBound(b) Then Exit Sub
' this bit will need amended to fit your needs but I'm using & concatenate to just make a string with the outputs
For i = LBound(a) To UBound(b)
str = str & i + 1 & "-" & a(i) & "-" & b(i) & vbNewLine
Next i
' proof in the pudding
MsgBox str
End Sub
Sub test()
Dim rngDB As Range
Dim vR() As Variant
Dim i As Long
Set rngDB = Range("a2", Range("a" & Rows.Count).End(xlUp)) '<~~personaliation Max Char data range
ReDim vR(1 To rngDB.Count, 1 To 1)
For i = 1 To rngDB.Count
vR(i, 1) = textjoin(rngDB(i), rngDB(i, 2))
Next i
Range("c2").Resize(rngDB.Count) = vR '<~ result wil be recorded in Column C
End Sub
Function textjoin(rng1 As Range, rng2 As Range)
Dim vS1, vS2
Dim vR()
Dim i As Integer
vS1 = Split(rng1, "x")
vS2 = Split(rng2, ",")
ReDim vR(UBound(vS1))
For i = LBound(vS1) To UBound(vS1)
vR(i) = i + 1 & "-" & Trim(vS1(i)) & "-" & Trim(vS2(i))
Next i
textjoin = Join(vR, ",")
End Function
THANK YOU FOR ALL OF THE HELP
I went back to the drawing board having seen the above.
I learnt
That my original use of array formula and TEXTJOIN where over the top and hardly simplistic
That I can use VBA just like any other programming code :)
My Solution simplified from Dy.Lee
Function SPLITANDMERGE(arr1 As String, arr2 As String, Optional del1 As String = "x", Optional del2 As String = ",")
'Arr1 Split'
Dim aS1
'Arr2 Split'
Dim aS2
'Value Array'
Dim r()
'Value Count'
Dim v As Integer
'Split The Values'
aS1 = Split(arr1, del1)
aS2 = Split(arr2, del2)
'Count The Values'
ReDim r(UBound(aS1))
'For All The Values'
For v = LBound(aS1) To UBound(aS2)
'Create The String'
r(v) = "Personalisation_Line " & v + 1 & " - " & Trim(aS1(v)) & " Characters - [" & Trim(aS2(v)) & "]"
Next v
'Join & Return'
SPLITANDMERGE = Join(r, ", ")
End Function
I'm still working on it but I now get the following result.
Will Be Adding:
Value Count Comparison (If we have 4 and 5 Values return "-" to be picked up by conditional formatting)
Conditional plural values (If value 2 in the string is 0 then character instead of characters
If there are any pitfalls or errors anyone can see please do enlighten me. Im here to learn.
I have a principal sheet (Launch Tracker) that needs to be updated from a database. I have put the extraction of the database on an adjacent sheet (LAT - Master Data).
What I would like to do is that if the value of the columns H, O, Q are similar then it would replace the lines from column "E" to "AL" on the (Launch Tracker), if there is no match I would like to add the entire line at the end of the (Launch Tracker) sheet.
I already have this code that was running when I made a test, but now it doesn't seem to be working and I cannot figure out why.
Option Explicit
Option Base 1
Dim Ttrak_concat, Tdata_concat, Derlig As Integer
Sub General_update()
Dim Cptr As Integer, D_concat As Object, Ref As String, Ligne As Integer, Lig As Integer
Dim Start As Single
Dim test 'for trials
Start = Timer
Application.ScreenUpdating = False
Call concatenate("LAT - Master Data", Tdata_concat)
Call concatenate("Launch Tracker", Ttrak_concat)
'collection
Set D_concat = CreateObject("scripting.dictionary")
For Cptr = 1 To UBound(Ttrak_concat)
Ref = Ttrak_concat(Cptr, 1)
If Not D_concat.exists(Ref) Then: D_concat.Add Ref, Ttrak_concat(Cptr, 2)
Next
'comparison between the sheets
Sheets("LAT - Master Data").Activate
For Cptr = 1 To UBound(Tdata_concat)
Ref = Tdata_concat(Cptr, 1) 'chaineIPR feuil data
Ligne = Tdata_concat(Cptr, 2) 'localisation sheet data
If D_concat.exists(Ref) Then
Lig = D_concat.Item(Ref) 'localisation sheet track
Else
Lig = Derlig + 1
End If
Sheets("LAT - Master Data").Range(Cells(Ligne, "E"), Cells(Ligne, "AL")).Copy _
Sheets("Launch Tracker").Cells(Lig, "E")
Next
Sheets("Launch Tracker").Activate
Application.ScreenUpdating = False
MsgBox "mise à jour réalisée en: " & Round(Timer - Start, 2) & " secondes"
End Sub
'---------------------------------------
Sub concatenate(Feuille, Tablo)
Dim T_coli, T_colp, T_colr, Cptr As Integer
Dim test
With Sheets(Feuille)
'memorizing columns H O Q
Derlig = .Columns("H").Find(what:="*", searchdirection:=xlPrevious).Row
T_coli = Application.Transpose(.Range("H3:H" & Derlig))
T_colp = Application.Transpose(.Range("O3:O" & Derlig))
T_colr = Application.Transpose(.Range("Q3:Q" & Derlig))
'concatenate for comparison
ReDim Tablo(UBound(T_colr), 2)
For Cptr = 1 To UBound(T_colr)
Tablo(Cptr, 1) = T_coli(Cptr) & " " & T_colp(Cptr) & " " & T_colr(Cptr)
Tablo(Cptr, 2) = Cptr + 2
Next
End With
End Sub
Would someone have the solution to my problem?
Thank you in advance :)
EDIT 11:48
Actually the code runs now but It doesn't work the way I need it to. I would like to update the information on my sheet Launch tracker from the LAT - Master data sheet when the three columns H, O and Q are the same. The problem is that I have checked and some lines present in the LAT - Master Data sheet are not being added into the Launch tracker sheet after running the macro... Does someone have any idea why ?
Agathe
A type mismatch means that you gave a function a parameter that has the wrong type. In your case that means that UBound can't deal with T_colr or ReDim can'T deal with UBound(T_colr). Since Ubound always returns an integer, it must be T_colr.
If Derlig=3 then Application.Transpose(.Range("Q3:Q" & Derlig)) won't return an array but a single value (Double, String or whatever). That's when UBound throws the error.
You will also get an error with T_coli(Cptr) etc.
What you could do to prevent this is to check if Derlig = 3 and treat that case individually.
If Derlig = 3 Then
ReDim Tablo(1, 2)
Tablo(1, 1) = T_coli & " " & T_colp & " " & T_colr
Tablo(1, 2) = 3
Else
ReDim Tablo(UBound(T_colr), 2)
For Cptr = 1 To UBound(T_colr)
Tablo(Cptr, 1) = T_coli(Cptr) & " " & T_colp(Cptr) & " " & T_colr(Cptr)
Tablo(Cptr, 2) = Cptr + 2
Next Cptr
End If
I have an application for which I am currently using a dictionary object (specifically, it's a dictionary of dictionaries of dictionaries, so each lookup has three steps, if that makes any sense!). I do a large number of lookups on these dictionaries and multiply the results together.
The problem is that in the previous version of the application, I used the VLookup function to accomplish this functionality, and it would error out when I would try to look up a key that didn't exist. Now, it returns a "Empty", which Excel is happy to multiply by whatever I already had and return a zero. This is hard to track, and I'd very much prefer for it to return an error like before.
Is there something I can change to get it to return the error like it would with a VLookup, or do I need to create a new class module to do this? A class module would likely require me to re-write a large amount of code, which I'd like to avoid (there are hundreds of lookups I would have to update in the code).
Thanks.
Here is some of my code:
This is the module I use to load in all the tables to the dictionary:
Sub LoadFactorsAndBaseRates()
Dim t As Double
t = Timer
Dim n As Name
Dim TempArray()
Dim dict1 As Dictionary
Dim dict2 As Dictionary
Dim i As Integer
Dim j As Integer
For Each n In ThisWorkbook.Names
If InStr(1, n.RefersTo, "#") <> 0 Or InStr(1, n.RefersTo, "\") Then GoTo skipname
If Not FactorLookup.Exists(n.Name) And n.RefersToRange.Parent.Name <> "Rate Matrix" And InStr(1, n.Name, "Print") = 0 And InStr(1, n.Name, "FilterDatabase") = 0 And n.Name <> "Policies" Then
Set dict1 = New Dictionary
On Error GoTo err1
TempArray = n.RefersToRange.Value
For j = 1 To n.RefersToRange.Columns.Count
On Error Resume Next
Set dict2 = New Dictionary
For i = 1 To UBound(TempArray, 1)
dict2.Add TempArray(i, 1), TempArray(i, j)
Next i
dict1.Add j, dict2
Next j
Erase TempArray
FactorLookup.Add n.Name, dict1
End If
skipname:
Next n
Exit Sub
err1:
If Err.number = 1004 Then Resume skipname
End Sub
And here is a sample of the lookup code:
CoverageColumn = 2
'Base Rate
Temp = FactorLookup("Base_Rates")(CoverageColumn)(State & "_" & Company & "_" & Terr)
If Vehicle <> "Snowmobile" Then
'Class 1
x = FactorLookup("Class1")(CoverageColumn)(State & "_" & Company & "_" & Class1)
Temp = xRound(Temp * x, 1)
'Class 2
x = FactorLookup("Class2")(CoverageColumn)(State & "_" & Company & "_" & Class2)
Temp = xRound(Temp * x, 1)
'Class 3
x = FactorLookup("Class3")(CoverageColumn)(State & "_" & Company & "_" & Class3)
Temp = xRound(Temp * x, 1)
'Class 4
x = FactorLookup("Class4")(CoverageColumn)(State & "_" & Company & "_" & Class4)
Temp = xRound(Temp * x, 1)
The code is basically just a bunch of pages of this: look up, multiply, round to the nearest tenth, repeat. Occasionally, there's a step where we add instead of multiplying.
The xRound function adds 0.0000001 and then uses the Round function to round to the indicated number of decimal places (to account for the weirdness of the Excel VBA round function).
You need to create a function to "wrap" your top-level dictionary so you can call it with the three "keys" and get back an error value if that combination doesn't exist.
Function DoFactorLookup(k1, k2, k3) As Variant
Dim d, d2, rv
rv = CVErr(xlErrNA) ' #N/A error value
If FactorLookup.exists(k1) Then
Set d = FactorLookup(k1)
If d.exists(k2) Then
Set d2 = d(k2)
If d2.exists(k3) Then
rv = d2(k3)
End If
End If
End If
DoFactorLookup = rv
End Function
Where I work we keep a list of vehicles that we find with damages. These damage codes come in a few variations, and I would like to setup a VBA script in excel to auto change the contents of a cell with the correct formatting, but I don’t really use VBA scripting and the Excel data objects confuse me
Here are a few examples of what I would like
06071 – VBA Function – 06.07.1
031211 – VBA Function- 03.12.1(1)
0409237-VBA Function – 04.09.2(3,7)
040912 030713 –VBA Function – 04.09.1(2) 03.07.1(3) (some vehicles have multiple damages)
Basically any number past length 5 would put any numbers in the 6th position onward into the parentheses, separated by commas.
I could do this in just about any other language, it’s just with all the random Excel stuff I am having issue after issue.
It doesn’t seem to matter what I try, my code bugs out before I can make any progress past
Dim test
test = Worksheets(“Sheet1”).Range(“A:A”).Value
Worksheets(“Sheet2”).Range(“B:B”).Value=test
I tried to make a function which ended up not working no matter how I called it. If I could just basic formatting of these numbers, I could more than likely figure it out from there.
Thanks for any help you guys can give me
You can do this with a UDF (user defined function): Place the following code in a new module in VBA:
Function ConvertIt(rng As Range) As String
Dim varStr As Variant
Dim strSource As String, strResult As String
Dim i As Integer
For Each varStr In Split(Trim(rng.Value), " ")
strSource = CStr(varStr)
strResult = strResult & _
Mid(strSource, 1, 2) & "." & _
Mid(strSource, 3, 2) & "." & _
Mid(strSource, 5, 1)
If Len(strSource) > 5 Then
strResult = strResult & "("
For i = 6 To Len(strSource)
strResult = strResult & Mid(strSource, i, 1) & ","
Next i
strResult = Left(strResult, Len(strResult) - 1) & ")"
End If
strResult = strResult & " "
Next
ConvertIt = Left(strResult, Len(strResult) - 1)
End Function
Assuming that your data is in column A of your worksheet, place this formula in B2: =ConvertIt(A2) and copy it down. Done!
If you want to convert the cells in one rush and replace the source, use this code:
Sub ConvertAll()
Dim rng As Range
For Each rng In Range("A1:A100")
rng.Value = ConvertIt(rng)
Next
End Sub
Lightly-tested:
Function FormatStuff(v)
Dim i As Long, c As String, v2 As String, num As String
Dim num2 As String, x As Long
v2 = v
v = v & " "
For i = 1 To Len(v)
c = Mid(v, i, 1)
If c Like "#" Then
num = num & c
Else
If num <> "" And Len(num) >= 5 Then
num2 = Left(num, 2) & "." & Mid(num, 3, 2) & _
"." & Mid(num, 5,1)
If Len(num) > 5 Then
num2 = num2 & "("
For x = 6 To Len(num)
num2 = num2 & IIf(x > 6, ",", "") & Mid(num, x, 1)
Next x
num2 = num2 & ")"
End If
v2 = Replace(v2, num, num2)
End If
num = ""
End If
Next i
FormatStuff = v2
End Function
To answer your unasked question:
There are two reasons the code you supplied does not work.
Range("A:A") and Range("B:B") both select entire rows, but the
test variable can only hold content for one cell value at a time.
If you restrict your code to just one cell, using
Range("A1").value, for example, the code you have written will
work.
It seems you used different quotation marks than the
standard, which confuses the compiler into thinking "Sheet1", "A:A". etc. are variables.
With the range defined as one cell, and the quotation marks replaced, your code moves the value of cell A1 on Sheet1 to cell B1 on Sheet2:
Sub testThis()
Dim Test
Test = Worksheets("Sheet1").Range("A1").value
Worksheets("Sheet2").Range("B1").value = Test
End Sub
If you wanted to work down the entire column A on Sheet1 and put those values into the column B on Sheet2 you could use a loop, which just repeats an action over a range of values. To do this I've defined two ranges. One to track the cells on Sheet1 column A, the other to track the cells on Sheet2 column B. I've assumed there is no break in your data in column A:
Sub testThat()
Dim CellinColumnA As Range
Set CellinColumnA = Worksheets("Sheet1").Range("A1")
Dim CellinColumnB As Range
Set CellinColumnB = Worksheets("Sheet2").Range("B1")
Do While CellinColumnA.value <> ""
CellinColumnB.value = CellinColumnA.value
Set CellinColumnA = CellinColumnA.Offset(1, 0)
Set CellinColumnB = CellinColumnB.Offset(1, 0)
Loop
End Sub