want to convert Excel formula into VBA code - excel

I wanted to convert below formula to VBA code.
=C1&"`"&K1&"`"&L1&"`"&J1
=VLOOKUP(M1,Data!$A:$J,9,)
=SUMPRODUCT(SUMIF(B1:B,B1,G1:G))
Currently i have enter this formula in 1st row and than copying this formula till the last row used which is taking lot time to apply formula because it has more than million row.
LR1 = Sheets("CRIMS").UsedRange.Rows.Count
Sheets("CRIMS").Range("M1:P1").AutoFill Destination:=Sheets("CRIMS").Range("M1:P" & LR1)
is there any way to convert this formula into VBA code?

For first formula the easiest way would be:
Range("M" & i).FormulaR1C1 = "=RC[-10]&""`""&K&""`""&L&""`""&J"
But for vlookup I prefer dictionaries/collections! It is much much faster.
If You have source data in Data sheet and You want to put that to CRIMS sheet to column M:
Sub vlookup()
Dim names As Range, values As Range
Dim lookupNames As Range, lookupValues As Range
Dim vlookupCol As Object
Dim lastRow As Long
Dim lastRow2 As Long
Dim objekt as Object
With Sheets("Data")
lastRow = Sheets("Data").Cells(Rows.Count, 1).End(xlUp).row
Set names = Sheets("Data").Range("A1:A" & lastRow)
Set values = Sheets("Data").Range("I1:A" & lastRow)
End With
Set objekt = BuildLookupCollection(names, values)
With Sheets("CRIMS")
lastRow2 = 1000000
Set lookupNames = .Range("M1:M" & lastRow)
Set lookupValues = .Range("N1:N" & lastRow)
End With
VLookupValues lookupNames, lookupValues, objekt
Set objekt = Nothing
End Sub
Function BuildLookupCollection(categories As Range, values As Range)
Dim vlookupCol As Object, i As Long
Set vlookupCol = CreateObject("Scripting.Dictionary")
On Error Resume Next
For i = 1 To categories.Rows.Count
Call vlookupCol.Add(CStr(categories(i)), values(i))
Next i
On Error GoTo 0
Set BuildLookupCollection = vlookupCol
End Function
Sub VLookupValues(lookupCategory As Range, lookupValues As Range, vlookupCol As Object)
Dim i As Long, resArr() As Variant
ReDim resArr(lookupCategory.Rows.Count, 1)
For i = 1 To lookupCategory.Rows.Count
resArr(i - 1, 0) = vlookupCol.Item(CStr(lookupCategory(i)))
Next i
lookupValues = resArr
End Sub

Quotation Marks need to be doubled in VBA
Try this:
For i = 1 To LR1
Range("M" & i).Formula = "=C" & i & "&""`""&K" & i & "&""`""&L" & i & "&""`""&J" & i
Range("N" & i).Formula = "=VLOOKUP(M" & i & ",Data!$A:$J,9,)"
Next i
(replace column letters with actual target column)
As mentioned in the comments Looping in this case is highly inefficient.
Use this Code to insert the formulas all at once. It still takes some time for 1 Milion rows though.
Range("M1:M" & LR1).Formula = "=C:C & ""`"" & K:K & ""`"" & L:L & ""`"" & J:J"
Range("N1:N" & LR1).Formula = "=VLOOKUP(M:M,Data!$A:$J,9,)"

Related

VBA to replace cell text that starts with ### to = in 3 different columns without affecting rows 1 to 15

I'm not very familiar with VBA, but am in need of a macro that would replace ### to = for columns B, F, and J without affecting rows 1 to 15.
I found the below code as my starting point, but can't seem to adjust it to my situation, or if this would even work for my situation...
this code was replacing everything in B that was not empty to Title starting at B2
Sub replace_text
Dim lrow As Long
Dim rng As Range
lrow = Cells(Rows.Count, "B").End(xlUp).Row 'Detect last data entry row in Column B.
For Each rng In Range("B2:B" & lrow)
If rng <> "" Then
rng = "Title"
End If
Next rng
End Sub
essentially, my goal is to replace every occurrences of text strings that start with ### with = without affecting the remainder of the strings.
everything in column B, F, and J starting at row 16 would start with ### but do not have the same text after the ###.
other columns would also have ### at the start but would need to remain intact
Worksheet.Evaluate: Replace Text
Sub ReplaceText()
Const FIRST_ROW As Long = 16
Const COLUMNS_RANGE As String = "B:B,F:F,J:J"
Const BEGINS_WITH_STRING As String = "###"
Const REPLACE_STRING As String = "="
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range
With ws.UsedRange
Set rg = .Resize(.Rows.Count + .Row - FIRST_ROW).Offset(FIRST_ROW - 1)
End With
Dim arg As Range, aAddress As String, Formula As String
For Each arg In Intersect(rg, ws.Range(COLUMNS_RANGE)).Areas
aAddress = arg.Address
Formula = "IF(LEFT(" & aAddress & "," & Len(BEGINS_WITH_STRING) _
& ")=""" & BEGINS_WITH_STRING & """," & "SUBSTITUTE(" _
& aAddress & ",""" & BEGINS_WITH_STRING & """,""" _
& REPLACE_STRING & """,1)," & aAddress & ")"
'Debug.Print Formula
arg.Value = ws.Evaluate(Formula)
Next arg
MsgBox "Text replaced.", vbInformation
End Sub

VBA macro: If Range Contains Words from Another Range Then Type x in Third Range

I would like to solve the following problem:
In Worksheet1 I have a range in text form from O3 to O4500. If the cells in this range contain certain words, I want an "x" to be put in the range U3:U4500 (in the same row). The words to be tested are in range B4:B15 in another Worksheet (Worksheet2).
I made it work with the following code (solution1), but now I don't want to type the code manually for word1, word2, words3... instead it should be taken from the other range in Worksheet 2 (see my draft below in solution2). I believe the problem are the "* *" which are missing when I use the referral to the other range.
Any help is very much appreciated!
Sub solution1()
Dim i As Long
For i = 3 To 4500
If LCase$(Worksheet1.Range("O" & i).Value) Like "*word1*" Or _
LCase$(Worksheet1.Range("O" & i).Value) Like "*word2*" Or _
LCase$(Worksheet1.Range("O" & i).Value) Like "*word3*" Then
Worksheet1.Range("U" & i).Value = "x"
End If
Next
End Sub
Sub solution2()
Dim i As Long, c As Long
For i = 3 To 4500
For c = 4 To 15
If LCase$(Worksheet1.Range("O" & i).Value) Like LCase$(Worksheet2.Range("B" & c).Value) Then
Worksheet1.Range("U" & i).Value = "x"
End If
Next
Next
End Sub
try something like:
Sub solution2()
Dim i As Long, c As Long
searchstring = LCase$(Worksheets("Worksheet2").Range("B1").Value & "|" & Worksheets("Worksheet2").Range("B2").Value & "|" & Worksheets("Worksheet2").Range("B3").Value)
For i = 2 To 9
If Len(LCase$(Worksheets("Worksheet1").Range("O" & i).Value)) < 1 Then GoTo neexxtt
'line above prevents empty lines to be marked
If InStr(searchstring, LCase$(Worksheets("Worksheet1").Range("O" & i).Value)) <> 0 Then Worksheets("Worksheet1").Range("U" & i).Value = "x"
neexxtt:
Next
End Sub
A VBA Lookup: Using an (Array)Formula For Partial Matches
In Excel, in cell U3, you could use the following array formula:
=IF(COUNT(MATCH("*"&Sheet2!$B$4:$B$15&"*",O3,0))>0,"X","")
and copy it down (adjust the lookup worksheet name (Sheet2)).
The following solution is based on this formula avoiding any loops.
Sub VBALookup()
Const Flag As String = "x"
' Reference the ranges.
Dim srg As Range ' Source
Dim drg As Range ' Destination
Dim lrg As Range ' Lookup
With Worksheet1
Set srg = .Range("O3", .Cells(.Rows.Count, "O").End(xlUp))
Set drg = srg.EntireRow.Columns("U")
End With
With Worksheet2
Set lrg = .Range("B4", .Cells(.Rows.Count, "B").End(xlUp))
End With
' Build the array formula.
Dim ArrayFormula As String
ArrayFormula = "=IF(COUNT(MATCH(""*""&'" & Worksheet2.Name & "'!" _
& lrg.Address & "&""*""," & srg.Cells(1).Address(0, 0) & ",0))>0,""" _
& Flag & ""","""")"
' Write the formulae (values).
With drg
' Write the array formula to the first cell.
.Cells(1).FormulaArray = ArrayFormula
' Autofill to the bottom.
.Cells(1).AutoFill .Cells, xlFillDefault
' Not sure, but instead of the previous 2 lines, in Office 365,
' the following single line should work:
'.Cells.Formula = ArrayFormula
' Convert to values (out-comment if you want to keep the formulae).
.Value = .Value
End With
End Sub

How to make exceptions in a copy of a row

I'm beginner at Macros I need to do a copy of rows but I have to exclude some columns. EntireRow is working but I need to exclude the columns I,G,H
Sub Macro1()
Dim RngToChk as Range, RngToPaste as Range
Set RngToCheck=Application.InputBox(Prompt:="enter range", Type:=8)
Dim strtofind as String
Inttofind=InputBox("Give your Indicator")
Dim i as long
For i = RngToChk.Rows.Count To 1 Step -1
If RngToChk(i).value=strtofind Then
RngToCheck(i).Offset(1).EntireRow.Insert
Set RngToPaste=RngToChk(i).Offset(1)
RngToPaste.EntireRow.Value=RngToChk(i).EntireRow.Value
RngToPaste.EntireRow.Font.Color=RGB(255,0,0)
End If
Next i
End Sub
Add this function to your module:
Function AlmostEntireRow(StartingPoint As Range) As Range
Dim Row As Long
Dim TargetSheet As Worksheet
Row = StartingPoint.Row
Set TargetSheet = StartingPoint.Worksheet
Set AlmostEntireRow = Union(TargetSheet.Range("A" & Row & ":F" & Row), TargetSheet.Range("J" & Row & ":GR" & Row))
End Function
When you are using it, replace
RngToPaste.EntireRow.Font.Color=RGB(255,0,0)
with
AlmostEntireRow(RngToPaste).Font.Color = RGB(255, 0, 0)
and so on.
The function builds a range from the input range, consisting of columns A to F and J to GR. Adjust as needed.
Update
The suggested method does not work when copying rows. Here is a copy method as well.
Sub CopyAlmostEntireRow(FromRow As Range, ToRow As Range)
Dim FromRange As Range
Dim ToRange As Range
Set FromRange = FromRow.Worksheet.Range("A" & FromRow.Row & ":F" & FromRow.Row)
Set ToRange = ToRow.Worksheet.Range("A" & ToRow.Row & ":F" & ToRow.Row)
ToRange.Value = FromRange.Value
Set FromRange = FromRow.Worksheet.Range("J" & FromRow.Row & ":GR" & FromRow.Row)
Set ToRange = ToRow.Worksheet.Range("J" & ToRow.Row & ":GR" & ToRow.Row)
ToRange.Value = FromRange.Value
End Sub
' Call with something like this:
CopyAlmostEntireRow RngToChk(i), RngToPaste

Loops and row references

The below code needs to run the =disvincenty formula on a loop, and the referenced cells $C$2,$D$2 need to go down one row each time in same column each time code block runs until ABF (Last row with data in column T.) then it exit's sub
For each row, it needs to run the formulas =Min and the two =small's in the same columns already referenced, but also dropping down one row at a time - the same as the =distvincenty, but values being pasted each time to preserve result.
So =distvincenty is looking at two criteria in cells next to each other on same row, compares to a list running down a column, applies the three other formulas to that row, and moves down.
Dim rng As Range
Dim row As Range
Dim cell As Range
Dim ABF As Integer
ABF = Sheet9.Range("T" & Rows.Count).End(xlUp).row
Set rng = Range("Q2:Q" & ABF)
For Each row In rng.Rows
For Each cell In row.Cells
Sheet9.Range("Q2").Formula = "=distVincenty($C$2,$D$2,$R2,$S2)/1609.344"
Sheet9.Range("Q2").Copy
Sheet9.Range("Q2:Q" & ABF).PasteSpecial xlPasteAll
Application.CutCopyMode = False
Sheet9.Range("E2").Formula = "=MIN(Q:Q)"
Sheet9.Range("H2").Formula = "=SMALL(Q:Q,2)"
Sheet9.Range("K2").Formula = "=SMALL(Q:Q,3)"
Next cell
Next row
UPDATE:
I now have this:
Dim ABF As Integer
Dim i As Integer
For i = 2 To Sheet9.Range("A" & Rows.Count).End(xlUp).row
ABF = Sheet10.Range("AC" & Rows.Count).End(xlUp).row
Sheet10.Range("AE3").Formula = "=distVincenty('Booking Workings'!$C$2,'Booking Workings'!$D$2,'User List'!$Z3,'User List'!$AA3)/1609.344"
Sheet10.Range("AE3").Copy
Sheet10.Range("AE3:AE" & ABF).PasteSpecial xlPasteAll
Application.CutCopyMode = False
Sheet9.Range("E" & i).Formula = "=MIN('User List'!AE:AE)"
Sheet9.Range("H" & i).Formula = "=SMALL('User List'!AE:AE,2)"
Sheet9.Range("K" & i).Formula = "=SMALL('User List'!AE:AE,3)"
Next i
End sub
The only problem I have is that each time this code runs, I need the $C$2 and $D$2 to change like the simpler formulas.
Can I do this?
Sheet10.Range("AE3").Formula = "=distVincenty('Booking Workings'!$C$"(I)",'Booking Workings'!$D$"(I)",'User List'!$Z3,'User List'!$AA3)/1609.344"
UPDATE
looks like this works:
"=distVincenty('Booking Workings'!$C$" & (i) & ",'Booking Workings'!$D$" & (i) & ",'User List'!$Z3,'User List'!$AA3)/1609.344"
This is hard to explain. Let me know if I'm close.
Sub Test()
Dim rng As Range
Dim row As Range
Dim cell As Range
Dim ABF As Integer
Dim arrValues
Dim count As Integer
ABF = Sheet9.Range("T" & Rows.count).End(xlUp).row
Set rng = Range("Q2:Q" & ABF)
arrValues = rng.Value
For Each cell In rng
Sheet9.Range("Q2:Q" & ABF).Formula = "=distVincenty($C$" & cell.row & ",$D$" & cell.row & ",$R2,$S2)/1609.344"
Sheet9.Range("E2").Formula = "=MIN(Q:Q)"
Sheet9.Range("H2").Formula = "=SMALL(Q:Q,2)"
Sheet9.Range("K2").Formula = "=SMALL(Q:Q,3)"
count = count + 1
arrValues(count) = cell.Value
Next cell
Sheet9.Range("Q2:Q" & ABF).Value = arrValues
End Sub

Convert Text to Rows instead of Text to Columns

I have a text string that is using the ^ symbol as a delimiter.
I need to separate the text into new rows rather than new columns.
I need to create new rows to not overwrite the next line of data below it.
Is this possible without using a macro? I'm not against using one, I just wouldn't know where to start to write it.
Below is a pic of some sample data. The top part is how it's listed, and the bottom (in yellow) is how I would like it.
Using Excel 2010 on Windows 7 Pro.
Thanks to those that responded. A friend was able to help by providing the following code:
Sub Breakout()
Application.ScreenUpdating = False
LR = Cells(Rows.Count, 1).End(xlUp).Row
For r = LR To 2 Step -1
Set MyCell = Cells(r, 1)
Arry = Split(MyCell.Value, "^")
For c = 0 To UBound(Arry)
If c > 0 Then MyCell.Offset(c, 0).EntireRow.Insert
MyCell.Offset(c, 0) = Arry(c)
Next c
Next r
End Sub
Could try something like this:
Sub reArrange()
Dim inFirstRng As Range
Dim inRng As Range
Dim inCur As Variant
Dim outFirstRng As Range
Dim outCurRng As Range
Dim ws As Worksheet
'CHANGE ARGUMENT TO YOUR SHEET NAME
Set ws = Worksheets("Sheet2")
With ws
'CHANGE ARGUMENT TO WHATEVER THE FIRST CELL OR YOUR DATA INPUT IS IN COLUMN A
Set inFirstRng = .Range("A3")
Set inRng = .Range(inFirstRng, inFirstRng.End(xlDown))
'CHANGE ARGUMENT TO WHATEVER THE FIRST CELL OR YOUR DATA OUTPUT IS IN COLUMN A
Set outFirstRng = .Range("A9")
Set outCurRng = outFirstRng
End With
For Each cell In inRng.Cells
inCur = WorksheetFunction.Transpose(Split(cell.Value, "^"))
outCurRng.Resize(UBound(inCur), 1).Value = inCur
With ws
.Range("G" & outCurRng.Row & ":L" & outCurRng.Row).Value = _
.Range("G" & cell.Row & ":L" & cell.Row).Value
End With
Set outCurRng = outCurRng.Offset(UBound(inCur), 0)
Next cell
ws.Range("F" & outFirstRng.Row & ":F" & outCurRng.Row - 1).Value = 1
End Sub

Resources