Simple Excel VBA takes ages - excel

I have written a simple VBA script (code below) that should inspect every cell in a certain column. Here I want to do some string manipulation ( i wante to search for "." in the string and then take the right side, but because I could not get it to work I always take the 4 digit as a start). I then copy the manipulated string into another cell and later back. The code works, but for some reason, it takes ages to run on only 35 cells!
I´m still a kook on VBA and wanted to get input what could be the reason for it and what I could improve to get a faster runtime. Is it because I take all strings froms 4 up to 50 ?
Sub EditStatus()
Application.DisplayAlerts = False
ActiveSheet.Name = "Backend"
myNum = Application.InputBox("Please enter the row number until which you would like to update the status column (only for new entries)")
For i = 2 To myNum
Cells(i, 20).Value = Mid(Cells(i, 10), 4, 50)
Cells(i, 10).Value = Cells(i, 20).Value
Next i
Range("T1:T200").Clear
Application.DisplayAlerts = True
End Sub
Thanks

No need for a loop. You can enter the formula in the entire range in 1 go and then convert them to values before putting the values back in Col J
Replace
For i = 2 To myNum
Cells(i, 20).Value = Mid(Cells(i, 10), 4, 50)
Cells(i, 10).Value = Cells(i, 20).Value
Next i
With
With Range("T2:T" & myNum)
.Formula = "=MID(J2, 4, 50)"
.Value = .Value
Range("J2:J" & myNum).Value = .Value
End With
Alternatively, you can directly perform the same action in Col J without the helper column T. For example you can do all that in 1 line as explained HERE as well
Simply replace
For i = 2 To myNum
Cells(i, 20).Value = Mid(Cells(i, 10), 4, 50)
Cells(i, 10).Value = Cells(i, 20).Value
Next i
Range("T1:T200").Clear
with
Range("J2:J" & myNum).Value = Evaluate("Index(MID(" & "J2:J" & myNum & ", 4, 50),)")

Replace Values In-Place
Adjust the values in the constants section.
This solution overwrites the data and doesn't use a helper column, but you can test it with one indicated near the end of the code.
Solve the renaming (Backend) part as needed.
The Code
Option Explicit
Sub EditStatus()
' Define constants.
Const sPrompt As String = "Please enter the row number until which you " _
& "would like to update the status column (only for new entries)"
Const sTitle As String = "Enter Number"
Const wsName As String = "Backend"
Const First As Long = 2
Const cCol As Long = 10 ' J
Const Delim As String = "."
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code.
' Require input.
Dim Last As Variant
Last = Application.InputBox( _
Prompt:=sPrompt, Title:=sTitle, Default:=First, Type:=1)
' Validate input.
If VarType(Last) = vbBoolean Then
MsgBox "You cancelled."
Exit Sub
End If
If Last < First Then
MsgBox "Enter a number greater than " & First - 1 & "."
Exit Sub
End If
If Int(Last) <> Last Then
MsgBox "Enter a WHOLE number greater than " & First - 1 & "."
Exit Sub
End If
' Define column range.
Dim rg As Range
Set rg = wb.Worksheets(wsName).Cells(First, cCol).Resize(Last - First + 1)
' Write values from column range to array.
Dim Data As Variant
If rg.Rows.Count > 1 Then
Data = rg.Value
Else
ReDim Data(1 To 1, 1 To 1): Data = rg.Value
End If
' Declare additional variables
Dim cValue As Variant ' Current Value
Dim i As Long ' Current Row (Array Row Counter)
Dim fPos As Long ' Current Delimiter Position
' Replace strings containing the delimiter, with the sub string
' to the right of it.
For i = 1 To UBound(Data)
cValue = Data(i, 1)
If Not IsError(cValue) Then
fPos = InStr(1, cValue, Delim)
If fPos > 0 Then
Data(i, 1) = Right(cValue, Len(cValue) - fPos)
End If
End If
Next i
' Maybe rather test with the following (writes to column 20 ("T")).
'rg.Offset(, 10).Value = Data
' Write values from array to column range.
rg.Value = Data
End Sub

Related

How to use multiple function/formula on VBA

I'm new using VBA and I'm trying to code into VBA but it didn't work so far, my timestamp data is not common and I got 10000+ rows to do the same formula (sometime excel just crash so i would like to try VBA)
timestamp that I tried split
Edit : add code
Sub Split_text_3()
Dim p As String
For x = 1 To 6 '---How do it until last cell?
Cells(x, 2).Value = Mid(Cells(x, 1).Value, 9, 2) 'combind in same cell
Cells(x, 3).Value = Mid(Cells(x, 1).Value, 5, 3) 'combind in same cell
Cells(x, 4).Value = Mid(Cells(x, 1).Value, 21, 4) 'combind in same cell
Cells(x, 5).Value = Mid(Cells(x, 1).Value, 12, 8)
Next x End Sub
and the data look like this (I tried to separate it first and then might try to combine them later)
image
Please, try the next function:
Function extractDateTime(strTime As String) As Variant
Dim arrD, d As Date, t As Date
arrD = Split(strTime, " ")
d = CDate(arrD(2) & "/" & arrD(1) & "/" & arrD(4))
t = CDate(arrD(3))
extractDateTime = Array(d, t)
End Function
It can be tested in the next way:
Sub testExtractDate()
Dim x As String, arrDate
x = "WED SEP 08 08:13:52 2021"
arrDate = extractDateTime(x)
Debug.Print arrDate(0), arrDate(1)
End Sub
If it returns as you need (I think, yes...), you can use the next function to process the range. It assumes that the column keeping the strings are A:A, and returns in C:D:
Sub useFunction()
Dim sh As Worksheet, lastR As Long, Arr, arrDate, arrFin, i As Long
Set sh = ActiveSheet
lastR = sh.Range("A" & sh.rows.count).End(xlUp).row
Arr = sh.Range("A2:A" & lastR).Value
If IsArray(Arr) Then
ReDim arrFin(1 To UBound(Arr), 1 To 2)
For i = 1 To UBound(Arr)
If Arr(i, 1) <> "" Then
arrDate = extractDateTime(CStr(Arr(i, 1)))
arrFin(i, 1) = arrDate(0): arrFin(i, 2) = arrDate(1)
End If
Next i
sh.Range("C2").Resize(UBound(arrFin), 2).Value = arrFin
Else
sh.Range("C2:D2").Value = extractDateTime(CStr(sh.Range("A2").Value))
End If
End Sub
I think I have another solution (not bulletproof) but it is simplier, quicker and code less solution (no offense FraneDuru!):
Sub DateStamp()
Dim arr, arr_temp, arr_new() As Variant
Dim i As long
'Take cells from selected all the way down to 1st blank cell
'and assign values to an array
arr = ThisWorkbook.ActiveSheet.Range(Selection, Selection.End(xlDown)).Value
ReDim Preserve arr_new(1 To UBound(arr), 1 To 2)
For i = 1 To UBound(arr)
'Make another array by spliting input string by whitespace delimiter (default)
arr_temp = Split(arr(i, 1))
'Construct values in desired "format"
arr_new(i, 1) = "'" & arr_temp(2) & "/" & arr_temp(1) & "/" & arr_temp(4)
arr_new(i, 2) = arr_temp(3)
Next i
'Paste result into Excel
Selection.Offset(0, 1).Resize(UBound(arr), 2) = arr_new
End Sub
All you have to do is to select the cell toy want to start with and run the macro! :)
Bellow also a picture with watches, so you can catch-up what is going on:

VBA translate column value in alphabet to a numeral

How do I convert the alpha part of an excel address "$C$2" to 3 and 2 so that I could re-use it in a cell object.
If it is "$E$4", then I need two separate values like 5 (for the letter E) and 4, so that I could reference that using the object - Cells(4,5)
Basically, I am trying to un-merge cells using this code below and that is where the need to get the numeral of the excel cell came about.
Sub UnMerge()
Dim i As Integer
Dim fromRange() As String
Dim toRange() As String
Dim temp() As String
ActiveSheet.UsedRange.MergeCells = False
fromRange() = Split(ActiveCell.Address, "$")
temp() = Split(Selection.Address, ":")
toRange() = Split(temp(1), "$")
For i = fromRange(2) To toRange(2)
If Cells(i, Range(temp(0)).Column) = "" Then
Cells(i, Range(temp(0)).Column) = Cells(i - 1, Range(temp(0)).Column).Value
End If
Next i
End Sub
Debug.Print Range("$E$4").Row & ", " & Range("A1").Column
changing and spliting strings to get to numbers is slow. Just use the selection.rows and selection.column:
Sub UnMerge()
Selection.MergeCells = False
With ActiveSheet
Dim i As Long
For i = Selection.Row To Selection.Rows.Count + Selection.Row - 1
If .Cells(i, Selection.Column) = "" Then
.Cells(i, Selection.Column) = .Cells(i - 1, Selection.Column).Value
End If
Next i
End With
End Sub

How to check whether the first array entry is empty in VBA

The below VBA code sets a range of cells as commentArray, removes any blanks from the array and creates a new, blank free array, called commentResults. I then want to declare the array.
There is a possibility, depending on my source data, that the array could then still be empty so the below doesn't work to declare
thisws.Cells(i, 19).Resize(columnsize:=UBound(commentResults) - LBound(commentResults) + 1).Value = commentResults
So I thought I would add a check (the if statement after the debug.print), that only declared the array if array(0) wasn't empty but I continuously get an error 9 which I can't resolve.
Dim commentArray(4) As Variant
commentArray(0) = Cells(24, 4).Value
commentArray(1) = Cells(25, 3).Value
commentArray(2) = Cells(26, 3).Value
commentArray(3) = Cells(27, 3).Value
'a and b as array loops
Dim a As Long, b As Long
Dim commentResults() As Variant
'loops through the array to remove blanks - rewrites array without blanks into commentArray
For a = LBound(commentArray) To UBound(commentArray)
If commentArray(a) <> vbNullString Then
ReDim Preserve commentResults(b)
commentResults(b) = commentArray(a)
b = b + 1
End If
Next a
Debug.Print b
If IsError(Application.Match("*", (commentResults), 0)) Then
Else
thisws.Cells(i, 19).Resize(columnsize:=UBound(commentResults) - LBound(commentResults) + 1).Value = commentResults
b = 0
End If
Any thoughts on why this might not work?
I have also tried:
If commentResults(0) <> vbNullString Then
thisws.Cells(i, 27).Resize(columnsize:=UBound(commentResults) - LBound(commentResults) + 1).Value = commentResults
End If
Sub CommentArray()
Dim Comments As Range, c As Range
Set Comments = Union(Cells(24, 4), Range(Cells(25, 3), Cells(27, 3)))
Dim commentResults() As Variant
Dim i As Long
i = 0
For Each cell In Comments
If cell.Value <> "" Then
ReDim Preserve commentResults(i)
commentResults(i) = cell.Value
i = i + 1
End If
Next cell
Dim debugStr As String
For i = LBound(commentResults) To UBound(commentResults)
debugStr = debugStr & commentResults(i) & Chr(10)
Next i
MsgBox debugStr
End Sub

extract specific set of digits from random strings in EXCEL VBA

Disclaimer- my case is specific, and in my case my code works because I know the pattern.
I was looking for an answer everywhere, and the codes I tried were not quite what I was looking for, this is my solution if you are looking for a set of numbers.
In my case, I was looking for 7 digits, starting with digit 1 in a a column with random strings, some string had the number some others didn't.
The number will appear in these three scenarios "1XXXXXX", "PXXXXXXXX", "PXXXXXXXXX"(this has more digits because there is a slash).
Here are the examples of strings:
9797 P/O1743061 465347 Hermann Schatte Earl Lowe
9797 Po 1743071 404440 Claude Gaudette Jose Luis Lopez
9817 1822037 463889 Jean Caron Mickelly Blaise
My Code
Sub getnum()
'i don't use explicit so i didn't declare everything
Dim stlen As String
Dim i As Integer
Dim arra() As String
Dim arran() As String
Orig.AutoFilterMode = False
Call BeginMacro
LastRow = Orig.Cells(Rows.Count, 1).End(xlUp).Row
Orig.Range("J2:J" & LastRow).Clear
'loop though column
For n = 2 To LastRow
celref = Orig.Cells(n, 4).Value
'split string on white spaces
arra() = Split(celref, " ")
'turn string to multiple strings
For counter = LBound(arra) To UBound(arra)
strin = arra(counter)
'remove white spaces from string
storage = Trim(strin)
lenof = Len(storage)
'if string has 9 characthers, check for conditions
If lenof = 9 Then
'position of first and last charachter
somstr = Mid(storage, 1, 1)
somot = Mid(storage, 9, 1)
If somstr = "P" Or somstr = "p" And IsNumeric(somot) = True Then
'removes Po or PO and keeps only 7 digits
storage = Right(storage, 7)
'stores in column J
Orig.Cells(n, 10).Value = storage
End If
ElseIf lenof = 10 Then
somstr = Mid(storage, 1, 1)
somot = Mid(storage, 10, 1)
'other conditions
If somstr = "P" Or somstr = "p" And IsNumeric(somot) = True Then
'removes Po or PO and keeps only 7 digits
storage = Right(storage, 7)
'stores in column J
Orig.Cells(n, 10).Value = storage
End If
End If
'eliminate comma within
arran() = Split(storage, ",")
If Orig.Cells(n, 10).Value <> storage Then
For counter2 = LBound(arran) To UBound(arran)
strin2 = arran(counter2)
storage2 = Trim(strin2)
'final condition if is 7 digits and starts with 1
If IsNumeric(storage2) = True And Len(storage2) = 7 Then
car = Mid(storage2, 1, 1)
If car = 1 Then
'stores in columns J at specific position
Orig.Cells(n, 10).Value = storage2
End If
Else
If isnumeric(orig.cells(n,10).value) =true and _
len(orig.cells(n,10).value = 7 then
orig.cells(n,10).value = orig.cells(n,10).value
else
Orig.Cells(n, 10).Value = "no po# in D"
End If
Next counter2
End If
Next counter
Next n
Call EndMacro
End Sub
you may try this
Option Explicit
Sub getnum()
Dim position As Variant
Dim cell As Range
With Worksheets("Orig") ' change it to your actual sheet name
With Intersect(.UsedRange, Columns("J"))
.Replace what:="P/O", replacement:="P/O ", lookat:=xlPart
For Each cell In .Cells
position = InStr(cell.Text, " 1")
If position > 0 Then cell.Value = Mid(cell.Value, position + 1, 7)
Next
End With
End With
End Sub
This code paste two formulas one in column G and one in column J). The first formula checks for a "P" in the first character of the cell in column 2 and if there is a "P" it extracts the last 7 characters in the string and puts them in column G. The second formula checks if there is not a "P" and if not extracts the last 7 characters in the string and puts them in column J.
Sub Extract()
Dim ws As Worksheet
Dim lRow As Long
Set ws = ThisWorkbook.Sheets("Sheet3")
lRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
ws.Range("G2:G" & lRow).Formula = "=IF(LEFT(B2)=""P"",(RIGHT(B2,7)),"""")"
ws.Range("J2:J" & lRow).Formula = "=IF(LEFT(B2)<>""P"",(RIGHT(B2, 7)),"""")"
End Sub
You may use the RegEx to extract the number in desired format.
Please give this a try...
Function Get10DigitNumber(ByVal Str As String) As String
Dim RE As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "1\d{6}"
End With
If RE.test(Str) Then
Get10DigitNumber = RE.Execute(Str)(0)
End If
End Function
Then if you want to use this function on the worksheet itself, assuming your string is in A2, try this...
=Get10DigitNumber(A2)
OR
You may use this function in another sub routine/macro like this...
Debug.Print Get10DigitNumber(<pass your string variable here>)
Edited Function:
Function Get10DigitNumber(ByVal Str As String) As String
Dim RE As Object, Matches As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "[Pp]?\/?[Oo]?(1\d{6})\b"
End With
If RE.test(Str) Then
Set Matches = RE.Execute(Str)
Get10DigitNumber = Matches(0).SubMatches(0)
End If
End Function
And use if as already described above.
After understanding what you were doing, I think this will work. Any feedback would be appreciated.
Dim cell As Range, LRow As Long
LRow = ActiveSheet.Cells(Rows.Count, 4).End(xlUp).Row
For Each cell In Range("D2:D" & LRow)
If cell.Value Like "*Po *" Then
cell.Offset(0, 6).Value = Split(cell.Value, " ")(2)
Else: cell.Offset(0, 6).Value = Split(cell.Value, " ")(1)
End If
Next cell
For Each cell In Range("J2:J" & LRow)
If Len(cell.Value) > 7 Then
cell.Value = Right(cell.Value, 7)
End If
Next

How to format excel data for Adwords

I'm looking for help to get my Excel sheet into a specific format to import into Adwords. I have a sheet with data in this format:
I need to get it into this format:
What makes this tricky is getting 3 lines for each SKU. One line contains the Ad Group creation, the next is text ad creation and then the Keyword and bid is on the next line.
Can someone please help me achieve this? I would greatly appreciate it.
Thanks!
hope this helps...
(For all)
In a worksheet, set the range names that you can see the in code
ReportAddress
theSiteShort
theSiteLong
First
Second
And use it to reference inside the book. (This is just to not use $A$1)
Follow the comments...
'Take the report and store the address in the
'Range("ReportAddress") to use it later...
Sub takeReport()
'this is to take the reporte and store the address in Cells C6
'that has the name ReportAddress as you can see
Dim i
Dim FD As FileDialog 'to take files
Set FD = Application.FileDialog(msoFileDialogFilePicker)
With FD 'invoke the file dialog
If .Show = -1 Then 'if you take any file (1 or more)
For Each i In .SelectedItems 'for each file do this...
Range("ReportAddress").Value = i 'and as you can see, just take one file...
Next i
End If
End With
End Sub
'to create the report
Sub do_Report()
Dim dirReport As String
Dim wrkData As Workbook
Dim shtData As Worksheet
Dim tmpReport As Workbook
Dim shtReport As Worksheet
Dim skuNum() 'to store the data
Dim skuName()
Dim vendorPartNum()
Dim manufacturer()
Dim r 'var for rows
Dim c
Dim col
Dim fil
Dim i
Dim counter
Dim shortSite As String
Dim longSite As String
Dim first As String
Dim second As String
'this is to a better handle of the data...
shortSite = Range("theSiteShort").Value
longSite = Range("theSiteLong").Value
first = Range("First").Value
second = Range("Second").Value
Workbooks.Open Range("ReportAddress").Value 'open the workbook with the data
Set wrkData = ActiveWorkbook
Set shtData = ActiveSheet 'here we can fail, because if the xls has more than 1 sheet and that sheet
'is not the sheet we need, could fail...
Workbooks.Add 'create a new workbook (CTRL+N)
Set tmpReport = ActiveWorkbook 'Store it here
Set shtReport = ActiveSheet 'as well the active sheet
'headlines
Range("A1").FormulaR1C1 = "Ad Group"
Range("B1").FormulaR1C1 = "Bid"
Range("C1").FormulaR1C1 = "Headline"
Range("D1").FormulaR1C1 = "Desc 1"
Range("E1").FormulaR1C1 = "Desc 2"
Range("F1").FormulaR1C1 = "Display URL"
Range("G1").FormulaR1C1 = "Final URL"
Range("H1").FormulaR1C1 = "Keyword"
wrkData.Activate 'got to the data report
shtData.Activate 'activate the sheet with the data, remember the comment!
r = Range("A1").End(xlDown).Row 'find the last row of data
c = Range("A1").End(xlToRight).Column 'As well the last column
For col = 1 To c 'well may is always the same qty of columns, but i like to count!!!
For fil = 2 To r 'for every row
Select Case col 'depends in which column is...
Case 1 'the first one use SkuNum... and so on... (This are the columns of the data source)
ReDim Preserve skuNum(1 To fil - 1)
skuNum(fil - 1) = Cells(fil, col)
Case 2
ReDim Preserve skuName(1 To fil - 1)
skuName(fil - 1) = Cells(fil, col)
Case 3
ReDim Preserve vendorPartNum(1 To fil - 1)
vendorPartNum(fil - 1) = Cells(fil, col)
Case 4
ReDim Preserve manufacturer(1 To fil - 1)
manufacturer(fil - 1) = Cells(fil, col)
Case Else
'do nothing 'just in case...
End Select
Next fil
Next col
tmpReport.Activate 'go to the new book, that is our final report
shtReport.Activate 'go to the sheet... just in case again... 'This line could be deletec
counter = 0 'a counter (index) for the vars()
For i = 1 To (r * 3) Step 3 '
'i got r lines and i need to put every 3 lines,
'then, that why I use Step 3 = (every 3th line), and that 3 * r.
counter = counter + 1
If counter > UBound(skuName) Then 'if the counter is bigger that the
'qty of vars inside SkuName (or any other)
Exit For 'get out the for loop!
End If
'here is the magic... almost...
Cells(i + 1, 1).Value = manufacturer(counter) & " - " & vendorPartNum(counter)
Cells(i + 1, 2).Value = first
Cells(i + 2, 1).Value = manufacturer(counter) & " - " & vendorPartNum(counter)
Cells(i + 2, 3).Value = manufacturer(counter) & " - " & vendorPartNum(counter) & " On Sale"
Cells(i + 2, 4).Value = skuName(counter)
Cells(i + 2, 5).Value = "Shop " & manufacturer(counter) & " now."
Cells(i + 2, 6).Value = shortSite & manufacturer(counter)
Cells(i + 2, 7).Value = longSite & skuNum(counter)
Cells(i + 3, 1).Value = manufacturer(counter) & " - " & vendorPartNum(counter)
Cells(i + 3, 2).Value = second
Cells(i + 3, 8).Value = "+" & manufacturer(counter) & " +" & vendorPartNum(counter)
Next i
Cells.EntireColumn.AutoFit 'Autofit all columns...
MsgBox "Ready!" 'Finish!
End Sub

Resources