VBA Rename spreadsheet based on specific cell value - vba

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

Related

Split two columns by delimiter and merge together taking a step from each (EXCEL 2016)

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.

Deleting duplicate text in a cell in excel

I was wondering how to remove duplicate names/text's in a cell. For example
Jean Donea Jean Doneasee
R.L. Foye R.L. Foyesee
J.E. Zimmer J.E. Zimmersee
R.P. Reed R.P. Reedsee D.E. Munson D.E. Munsonsee
While googling, I stumbled upon a macro/code, it's like:
Function RemoveDupes1(pWorkRng As Range) As String
'Updateby20140924
Dim xValue As String
Dim xChar As String
Dim xOutValue As String
Set xDic = CreateObject("Scripting.Dictionary")
xValue = pWorkRng.Value
For i = 1 To VBA.Len(xValue)
xChar = VBA.Mid(xValue, i, 1)
If xDic.exists(xChar) Then
Else
xDic(xChar) = ""
xOutValue = xOutValue & xChar
End If
Next
RemoveDupes1 = xOutValue
End Function
The macro is working, but it is comparing every letter, and if it finds any repeated letters, it's removing that.
When I use the code over those names, the result is somewhat like this:
Jean Dos
R.L Foyes
J.E Zimers
R.P edsDEMuno
By looking at the result I can make out it is not what I want, yet I got no clue how to correct the code.
The desired output should look like:
Jean Donea
R.L. Foye
J.E. Zimmer
R.P. Reed
Any suggestions?
Thanks in Advance.
Input
With the input on the image:
Result
The Debug.Print output
Regex
A regex can be used dynamically iterating on the cell, to work as a Find tool. So it will extract only the shortest match. \w*( OUTPUT_OF_EXTRACTELEMENT )\w*, e.g.: \w*(Jean)\w*
The Regex's reference must be enabled.
Code
Function EXTRACTELEMENT(Txt As String, n, Separator As String) As String
On Error GoTo ErrHandler:
EXTRACTELEMENT = Split(Application.Trim(Mid(Txt, 1)), Separator)(n - 1)
Exit Function
ErrHandler:
' error handling code
EXTRACTELEMENT = 0
On Error GoTo 0
End Function
Sub test()
Dim str As String
Dim objMatches As Object
Set objRegExp = CreateObject("VBScript.RegExp") 'New regexp
lastrow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
For Row = 1 To lastrow
str = Range("A" & Row)
F_str = ""
N_Elements = UBound(Split(str, " "))
If N_Elements > 0 Then
For k = 1 To N_Elements + 1
strPattern = "\w*(" & EXTRACTELEMENT(CStr(str), k, " ") & ")\w*"
With objRegExp
.Pattern = strPattern
.Global = True
End With
If objRegExp.test(strPattern) Then
Set objMatches = objRegExp.Execute(str)
If objMatches.Count > 1 Then
If objRegExp.test(F_str) = False Then
F_str = F_str & " " & objMatches(0).Submatches(0)
End If
ElseIf k <= 2 And objMatches.Count = 1 Then
F_str = F_str & " " & objMatches(0).Submatches(0)
End If
End If
Next k
Else
F_str = str
End If
Debug.Print Trim(F_str)
Next Row
End Sub
Note that you can Replace the Debug.Print to write on the target
cell, if it is column B to Cells(Row,2)=Trim(F_str)
Explanation
Function
You can use this UDF, that uses the Split Function to obtain the element separated by spaces (" "). So it can get every element to compare on the cell.
Loops
It will loop from 1 to the number of elements k in each cell and from row 1 to lastrow.
Regex
The Regex is used to find the matches on the cell and Join a new string with the shortest element of each match.
This solution operates on the assumption that 'see' (or some other three-letter string) will always be on the end of the cell value. If that isn't the case then this won't work.
Function RemoveDupeInCell(dString As String) As String
Dim x As Long, ct As Long
Dim str As String
'define str as half the length of the cell, minus the right three characters
str = Trim(Left(dString, WorksheetFunction.RoundUp((Len(dString) - 3) / 2, 0)))
'loop through the entire cell and count the number of instances of str
For x = 1 To Len(dString)
If Mid(dString, x, Len(str)) = str Then ct = ct + 1
Next x
'if it's more than one, set to str, otherwise error
If ct > 1 Then
RemoveDupeInCell = str
Else
RemoveDupeInCell = "#N/A"
End If
End Function

VBA : i = i + 1 counter not working

I'am on VBA trying to create a macro that record in different cell the number of times the workbook is open. Each time it is open, it creates a new cell with the number of the opening. So I created a counter for that.
Option Explicit
Dim i As Integer
Private Sub Workbook_Open()
If i = "" Then
i = 1
End If
ThisWorkbook.Worksheets("Feuil1").Cells(i, 1).Value = "Session " & i
i = i + 1
Debug.Print i
End Sub
However, the statement If i = "" remains highlighted in yellow, and I don't understand why...
Would someone have a solution?
Thank you !
i is an Integer so comparison to an empty String is not valid.
The closest thing you can do is
If i = 0 Then
But if you want the value to i to persist when you save the workbook, the simplest thing to do is to read its value from the workbook.
Of course you cannot assign a string to an integer. This is a type mismatch error.
What you need is to fetch the last cell in the column and then increment it.
Private Sub Workbook_Open()
' Fetch the last cell from column 1
Dim r As Range
With ThisWorkbook.Worksheets("Feuil1")
Set r = .Cells(.Rows.Count, 1).End(xlUp)
End With
' Get the last session number, or 0 if not present or doesn't match
Dim lastSession As Long: lastSession = 0
If Not IsEmpty(r) Then
Dim p As Long: p = InStr(1, Trim(r.value), "Session ")
If p = 1 Then
lastSession = CLng(Mid(r.value, p + Len("Session "), 10000))
End If
End If
r.Offset(1).value = "Session " & (lastSession + 1)
End Sub
I'd go like follows
Private Sub Workbook_Open()
With Worksheets("Feuil1")
With .Cells(.Rows.Count, 1).End(xlUp)
.Offset(1) = "Session " & Val(Replace(.Value, "Session ", "")) + 1
End With
End With
End Sub

Excel VBA: Can I name a range using a formula?

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.

Renaming a sheet based of specific words in a cell

I want to make a macro that will take the value in a cell and rename the sheet using this value. I only want a part the cell value to be used as the sheet name.
For example, in cell A1, it says "John Doe"... I want the macro to name that sheet "Doe, J.". How would I do that?
I already know how to name the sheet based off the entire cell.
Sub RenameSheet()
Dim rs As Worksheet
For Each rs In Sheets
rs.Name = rs.Range("B5")
Next rs
End Sub
You need to parse the string with the name first, and hence check if the sheet exists already (if so, adding a counter and make sure you don't name two sheets with the same name). Something like this:
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("B5"), " ")(1) + ", " + Left(Split(rs.Range("B5"), " ")(0), 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