I'm trying to use vlookup() function under for loop condition, but the value only follow only the first row value. This is my code. Sorry, the code is quite messy; I'm still learning VBA.
Sub vlookup_Click()
Application.ScreenUpdating = False
Dim result As String
Dim i As Long
Dim iLast As Long
Dim result1 As String
Dim sheet As Worksheet
Dim sheet1 As Worksheet
Dim WrkSht As String
WrkSht = "Sheet1"
iLast = ActiveWorkbook.Worksheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Set sheet = ActiveWorkbook.Sheets("Sheet1")
Set sheet1 = ActiveWorkbook.Sheets("InventoryReport")
For i = 10 To iLast
result = Application.WorksheetFunction.VLookup(sheet.Range("$B$10"), _
sheet1.Range("$B$10:$Q$48"), 16, False)
Sheets(WrkSht).Cells(i, 9).Value = result
Next i
End Sub
Below picture shows the result.Any idea to solve this?
Expected and Current Result:
So let's consider dumping the VLOOKUP() option. We are using VBA, so we have more power when it comes to looking up ranges.
I think we should go with using the range.Find() method here. Set this object, use the Row() property to match the Q column on that range, and return it to your I column in ws1.
Try this
Sub vlookup_Click()
Application.ScreenUpdating = False
Dim i As Long
Dim iLast As Long
Dim ws1 As Worksheet, ws2 As Worksheet
Dim findRng As Range
Set ws1 = ThisWorkbook.Sheets("Sheet1")
Set ws2 = ThisWorkbook.Sheets("InventoryReport")
'Changed ActiveWorksheet to ws1 for your iLast
iLast = ws1.Range("A" & Rows.Count).End(xlUp).Row
For i = 10 To iLast
Set findRng = ws2.Range("B:B").Find(ws1.Cells(i, "B"), , xlValues, xlWhole)
If Not findRng Is Nothing Then
ws1.Cells(i, "I") = ws2.Cells(findRng.Row, "Q")
Else
ws1.Cells(i, "I") = 0
End If
Set findRng = Nothing
Next i
Application.ScreenUpdating = True
End Sub
I see you use the excel worksheet function, but use the form of formulaR1C1 will be better. And record the Macro will free you from calculate the row# for the formula.
how to do:
You can just video this operation to get that code in your excel by click Tab "Developer" in your
excel/"record Macro" then input the formular in the cell then "stop recording" then Alt+F11 to get to
access the code of the vlookup formula:
ActiveCell.FormulaR1C1 = "=VLOOKUP(R10C2,R[3]C[-1]:R[41]C[11],16,0)"
Related
I have looked for over a day here in Stackoverflow and cannot find an answer to what I am trying to do.
All I need is a vba code that Vlookups and return the multiple results,
Eg; the lookup value is in sheet1 A1, data is in sheet2 columns A1:B40000, match the values in sheet2 A1:A40000 and returns the values from Sheet2 column B1:B40000.
Note:Its possible to find upto 5000 matches in sheet2 A1:A40000.
I have tried several ways to do this, such as Array formula (VERY SLOW), UDF (SLOW), VBA-AutoFilter(SLOW).
Is there any way to do this quickly?
Can anyone help?
Thanks a lot in advance!
Code tested with 40,000 entries, and this completes basically instantly:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wb As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim vLoookupVal As Variant
Dim vValues As Variant
Dim aResults() As Variant
Dim lResultCount As Long
Dim i As Long
Dim lIndex As Long
Set wb = ActiveWorkbook
Set ws1 = Me 'This is the sheet that contains the lookup value
Set ws2 = wb.Sheets("Sheet2") 'This is the sheet that contains the table of values
Application.EnableEvents = False
If Not Intersect(Target, ws1.Range("A1")) Is Nothing Then
ws1.Columns("B").ClearContents 'Clear previous results
vLoookupVal = Intersect(Target, ws1.Range("A1")).Value
lResultCount = WorksheetFunction.CountIf(ws2.Columns("A"), Target.Value)
If lResultCount = 0 Then
MsgBox "No matches found for [" & vLoookupVal & "]", , "No Matches"
Else
ReDim aResults(1 To lResultCount, 1 To 1)
lIndex = 0
vValues = ws2.Range("A1:B" & ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row).Value
For i = LBound(vValues, 1) To UBound(vValues, 1)
If vValues(i, 1) = vLoookupVal Then
lIndex = lIndex + 1
aResults(lIndex, 1) = vValues(i, 2)
End If
Next i
ws1.Range("B1").Resize(lResultCount).Value = aResults
End If
End If
Application.EnableEvents = True
End Sub
Maybe your AutoFilter code wasn't like this one?
Private Sub Main()
Dim lookUpVal As Variant
lookUpVal = Worksheets("Sheet1").Range("A1").Value
With Worksheets("Sheet2")
With .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
If WorksheetFunction.CountIf(.Cells, lookUpVal) = 0 Then Exit Sub
.AutoFilter field:=1, Criteria1:= lookUpVal
.Resize(,2).SpecialCells(xlCellTypeVisible).Copy Worksheets("Sheet1").Range("B1")
End With
.AutoFilterMode= False
End With
End Sub
Pivot table would speed things up and you can use the filter as the search function?
I have to find and replace rows in sheet 1 given matching cell value in the same column in sheet2. The column number is 4.
HELPPP!!!
This is what I have right now and I get an error on next x.
Sub DeleteRows()
Dim wb As Workbook
Dim ws As Worksheet
Dim ws2 As Worksheet
Set wb = ActiveWorkbook
Set ws = Sheets(Sheet1)
Set ws2 = Sheets(sheet2)
With wb
For i = 1 To ws2.Cells(Rows.Count, 4).End(xlUp).Row
Dim lookupvalue As String
lookupvalue = ws2.Cells(i, 4).Value
For x = 1 To ws1.Cells(Rows.Count, 4).End(xlUp).Row
Dim rng As range
For Each rng In range("D:D")
If InStr(1, rng.Value, "lookupvalue") > 0 Then
rng.Delete
End If
Next x
exitloop:
Next i
End With
End Sub
As A.S.H. said, the code needs a little improvement:
1) The two inner loops need to be combined.
2) The new inner loop should go from the bottom up, due to the fact that you are deleting the cell, This is probably why you have the second inner loop but that just adds time to the sub.
3) you are currently only deleting the one cell at a time, any data around it will remain. This may be what you want and so I left it, but if you meant to delete the entire row then uncomment the line that does that.
4) when testing with the instr function the variable should not be in quotes, with the variable in quotes it will look for that specific word "lookupvalues" and not the value assigned to that variable.
5) The with block that was being used did nothing. when using the with block the line that use it need to start with a '.' for example: on your code the with was with the workbook so every time a worksheet is used it should start with a "." like .ws1... and so forth. But by declaring the sheets using the workbook, this is no longer needed.
Sub DeleteRows()
Dim wb As Workbook
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim rng As Range
Dim lookupvalue As String
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Sheet1")
Set ws2 = wb.Sheets("sheet2")
For i = 1 To ws2.Cells(Rows.Count, 4).End(xlUp).Row
lookupvalue = ws2.Cells(i, 4).Value
For x = ws.Cells(Rows.Count, 4).End(xlUp).Row To 1 Step -1
Set rng = ws.Cells(x, 4)
If InStr(1, rng.Value, lookupvalue) > 0 Then
rng.Delete 'this only deletes the cell
'You may want this instead
'rng.entirerow.delete
End If
Next x
Next i
End Sub
I would like to propose an alternative way to handle this using a For Each Loop and the Find Method of the Range object.
Sub DeleteRows()
Dim wb As Workbook
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim lookup_rng As Range
Dim lookupvalue As String
Dim search_rng As Range
Dim rng As Range
Dim match_rng As Range
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Sheet1")
Set ws2 = wb.Sheets("Sheet2")
Set lookup_rng = Application.Intersect(ws2.Range("D:D"), ws.UsedRange)
Set search_rng = Application.Intersect(ws.Range("D:D"), ws2.UsedRange)
For Each rng In lookup_rng.Cells
lookupvalue = rng.Value
With search_rng
Set match_rng = .Find(lookupvalue, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlPrevious)
Do Until NoMoreMatches(match_rng)
match_rng.Delete 'Or match_rng.EntireRow.Delete if you want to delete the entire row.
Set match_rng = .FindPrevious
Loop
End With
Next
End Sub
Private Function NoMoreMatches(MatchRng As Range) As Boolean
NoMoreMatches = MatchRng Is Nothing
End Function
This approach is a little bit more wasteful then that of Scott Craner since the Find method always starts from the end of the range. However, I think it has the advantage that it is easier to read, i.e. that the code more directly shows what it is supposed to do.
Moreover, using this version you could extract the loops into a separate Sub you can use for arbitrary lookup and search ranges.
I am trying to loop through all the worksheets in the activeworkbook to perform a repetitive task.
I currently have the code below:
Sub sort_sectors()
Dim i As Integer
Dim rng As Range
Dim SortRng As Range
Dim rng1 As Integer
Dim ws As Worksheet
Dim wb As Workbook
Dim LastCol As Long
Dim LastRow As Long
Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
'This is marking several of the sheets of which I do not want to run the sub
If ws.Range("a9").Value = "x" Then
NextIteration:
End If
'Reference point is rng1 to select the desired range
With Range("a1:t100")
rng1 = .Find(what:="sector", LookIn:=xlValues).Row
End With
'return the row number for the sector header
LastCol = ws.Cells(20, ws.Columns.Count).End(xlToLeft).Column
LastRow = ws.Range("a15").End(xlDown).Row
'I am going to add the code below to finish out the task that I want to complete
Next
End Sub
I am sure the problem is that I'm misunderstanding something about how the for each loop actually works. Hopefully someone's answer will allow to better understand.
I really appreciate any help on this.
I made some edits to the code, and now I actually do have an error :) I tried making the changes you suggested for the "with ws.range etc..." piece of the code, and I get the object error 91.
Below is my new and "improved" code.
Sub sort_sectors()
Dim i As Integer
Dim rng As Range
Dim SortRng As Range
Dim intAnchorRow As Integer
Dim intMktCapAnchor As Integer
Dim intSectorAnchor As Integer
Dim ws As Worksheet
Dim wb As Workbook
Dim LastCol As Long
Dim LastRow As Long
Set wb = ActiveWorkbook
For Each ws In ActiveWorkbook.Worksheets
'Filter out the sheets that we don't want to run
If ws.Range("a9").Value <> "x" Or ws.Name = "__FDSCACHE__" Or ws.Name = "INDEX" Then
'Get the anchor points for getting sort range and the sort keys
''''''THIS IS THE PART THAT IS NOW GIVING ME THE ERROR'''''''
With ws.Range("a1:t100")
intAnchorRow = .Find(what:="sector", LookIn:=xlValues).Row
intSectorAnchor = .Find(what:="sector", LookIn:=xlValues).Column
intMktCapAnchor = .Find(what:="Market Cap", LookIn:=xlValues).Column
End With
'Find the last row and column of the data range
LastCol = ws.Cells(20, ws.Columns.Count).End(xlToLeft).Column
LastRow = ws.Range("a15").End(xlDown).Row
Set SortRng = Range(Cells(intAnchorRow + 1, 1), Cells(LastRow, LastCol))
Range(SortRng).Sort key1:=Range(Cells(intAnchorRow + 1, intSectorAnchor), Cells(LastRow, intSectorAnchor)), _
order1:=xlAscending, key2:=Range(Cells(intAnchorRow + 1, intMktCapAnchor), Cells(LastRow, intMktCapAnchor)), _
order2:=xlDescending, Header:=xlNo
End If
Next
End Sub
Thanks again. This has been very helpful for me.
If I've understood your issue correctly, you don't want to use a worksheet with an x in cell A9.
If that's the case I would change the condition of the if statement to check if the cell does not contain the x. If this is true, it enters the rest of the code. If not, it goes to the next iteration.
Also, your NextIteration: doesn't do anything in the If statement.
Sub sort_sectors()
Dim i As Integer
Dim rng As Range
Dim SortRng As Range
Dim rng1 As Integer
Dim ws As Worksheet
Dim wb As Workbook
Dim LastCol As Long
Dim LastRow As Long
Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
'This is marking several of the sheets of which I do not want to run the sub
If ws.Range("a9").Value <> "x" Then
'Reference point is rng1 to select the desired range
With Range("a1:t100")
rng1 = .Find(what:="sector", LookIn:=xlValues).Row
End With
'return the row number for the sector header
LastCol = ws.Cells(20, ws.Columns.Count).End(xlToLeft).Column
LastRow = ws.Range("a15").End(xlDown).Row
'I am going to add the code below to finish out the task that I want to complete
End If
Next
End Sub
The : operator is used to return the code to that line after a goto call.
For example
sub gotoEx()
for i = 1 to 10
if i = 5 then
goto jumpToHere
end if
next i
jumpToHere: '<~~ the code will come here when i = 5
'do some more code
end sub
And of course you can use this structure in your code if you wish, and have the jumpToHere: line just before the next
e.g.
for each ws in wb.Worksheets
if ws.Range("a9").Value = "x" then
goto jumpToHere
end if
'the rest of your code goes here
jumpToHere:
next
I have a VBA code that copies and pastes pages and copies everything properly except any cell that has a vlookup. I don't know what the problem is or if there may be some lag time. Any ideas would be helpful.
Dim thisSheet As Worksheet
Set thisSheet = Application.ActiveSheet
Dim templateSheet As Worksheet
Set templateSheet = Worksheets("Template ")
Dim baseSheet As Worksheet
Set baseSheet = Worksheets("Base")
Dim wrkBook As Workbook
Set wrkBook = ActiveSheet.Parent
Dim wrkSheet As Worksheet
Dim sNewSheetName As String
Dim nRow As Integer
Application.ScreenUpdating = False
Dim rRunRange As Range
Set rRunRange = Range("Cohorts_to_Run")
Dim n As Integer
For nRow = 1 To rRunRange.Rows.Count
sNewSheetName = rRunRange.Cells(nRow, 2).Value
Set wrkSheet = Worksheets.Add(, Worksheets(Worksheets.Count))
wrkSheet.Name = sNewSheetName
'Set parameters to pickup data....
Dim zZoom As Double
zZoom = ActiveWindow.Zoom
templateSheet.Activate
templateSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).Copy
wrkSheet.Activate
wrkSheet.PasteSpecial xlPasteColumnWidths
wrkSheet.Paste
ActiveWindow.Zoom = baseSheet.Range("ZOOM_LEVEL").Value
wrkSheet.Range("A1").RowHeight = 10
Application.CutCopyMode = False
wrkSheet.Range("B2").Value = Range("Cohorts_to_Run").Cells(nRow, 1).Value
Dim sTitleRangeName As String
sTitleRangeName = sNewSheetName & "_Title"
wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).Select
Selection.Copy
wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).PasteSpecial
xlPasteValuesAndNumberFormats
Next nRow
I assume your confusion is that last line which pastes only values and removes any formulas.
wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).PasteSpecial xlPasteValuesAndNumberFormats
Why not just paste? xlPasteValuesAndNumberFormats will remove your formulas everytime. That's the point.
Dim copyRange as Range, pasteRange as Range
Set copyRange = wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value)
Set pasteRange = wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value)
copyRange.Copy pasteRange
You're wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value) syntax is confusing though. Is there the name of another range in your named range TEMPLATE_COPY_RANGE?
Created a macro below thanks to help from another that works.
Basically, it takes the value of the cell in column A and, if a sheet doesn't exist with that cells name, creates it. Then it pastes all rows of data that have the corresponding cell value to that sheet. Ie. if a cell contains the following:
column a column b
dc00025 data value
If dc00025 doesn't exist, it'll make the sheet. And paste all rows with dc00025 in A.
This works perfectly. However, I noticed when you run this macro after a sheet has already been created, for some reason it adds thousands of columns dramatically slowing down excel.
To fix this, would it be possible to modify the script to only copy columns b:o rather tahnt he entire row? Pasting them starting at A3 would be preferable but I'm not sure how to fix that.
Thanks in advance.
Sub CopyCodes()
Application.ScreenUpdating = False
Dim rCell As Range
Dim lastrow As Long
lastrow = Sheets("Data").UsedRange.Rows.Count
For Each rCell In Worksheets("Data").Range("A2:A" & lastrow).SpecialCells(xlCellTypeConstants)
If Not SheetExists(rCell.Value) Then
With Worksheets.Add(, Worksheets(Worksheets.Count))
.Name = rCell.Value
End With
End If
Worksheets("Data").Rows(1).EntireRow.Copy Worksheets(rCell.Value).Rows(1)
Worksheets(rCell.Value).Range("A" & Rows.Count).End(xlUp)(2).EntireRow.Value = _
rCell.EntireRow.Value
Next rCell
Application.ScreenUpdating = True
End Sub
Function SheetExists(wsName As String)
On Error Resume Next
SheetExists = Worksheets(wsName).Name = wsName
End Function
Suggested fix:
Sub CopyCodes()
Application.ScreenUpdating = False
Dim rCell As Range
Dim lastrow As Long
Dim shtData as worksheet, shtDest as worksheet
Dim sheetName as string
set shtData=worksheets("Data")
lastrow = shtData.cells(rows.count,1).end(xlup).row
For Each rCell In shtData.Range("A2:A" & lastrow).SpecialCells(xlCellTypeConstants)
sheetName = rCell.Value
If Not SheetExists(sheetName) Then
set shtDest = Worksheets.Add(, Worksheets(Worksheets.Count))
shtDest.Name = sheetName
shtData.Rows(1).EntireRow.Copy shtDest.Rows(1)
Else
set shtDest = Worksheets(sheetName)
End If
shtDest.Range("A" & Rows.Count).End(xlUp).offset(1,0).EntireRow.Value = _
rCell.EntireRow.Value
Next rCell
Application.ScreenUpdating = True
End Sub