I'm trying to program a VLookup Table in VBA that references another file. Here is a simple outline of my goal:
Look up value in cell A2 in another Excel file
Pull the information in from column 2 of the other Excel file and place in Cell B2
Move on to cell A3 and repeat the process until there are no more entries left in column A
Here is the code that I already have. I keep getting an error that says "Unable to get the VLookup property of the WOrksheetFunction class." I checked the other posts referencing that error but they were not of any help. Do you all see an error in my code? Or does anyone have a better way of accomplishing this task?
Sub SBEPlannerAdder()
Dim wbk As Workbook
Set wbk = Workbooks.Open("C:\Users\user\Documents\Support File\Planner.xlsx")
With Sheets("Sheet1")
' Selects the first cell to check
Range("A2").Select
Dim x As Variant
x = wbk.Worksheets("Sheet1").Range("A1:C1752")
' Loops through all rows until an empty row is found
Do Until IsEmpty(ActiveCell)
Range(ActiveCell.Offset(0, 1) & ActiveCell.Row).Value = Application.WorksheetFunction.VLookup((ActiveCell.Column & ActiveCell.Row), x, 2, 0)
ActiveCell.Offset(1, 0).Select
Loop
End With
Call wbk.Close(False)
End Sub
When you open a workbook, it becomes the active workbook. It seems you were never passing control back to the target workbook.
Sub SBEPlannerAdder()
Dim rw As Long, x As Range
Dim extwbk As Workbook, twb As Workbook
Set twb = ThisWorkbook
Set extwbk = Workbooks.Open("C:\Users\user\Documents\Support File\Planner.xlsx")
Set x = extwbk.Worksheets("Sheet1").Range("A1:C1752")
With twb.Sheets("Sheet1")
For rw = 2 To .Cells(Rows.Count, 1).End(xlUp).Row
.Cells(rw, 2) = Application.VLookup(.Cells(rw, 1).Value2, x, 2, False)
Next rw
End With
extwbk.Close savechanges:=False
End Sub
See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.
It depends on whether you plan to do this as a one off or repeatedly. I'm assuming repeatedly since doing this manually is not all that difficult.
The first thing I would look at is your arguments. The first two should be ranges. So to be clear, perhaps you could do something like
Dim x As Range
set x = wbk.Worksheets("Sheet1").Range("A1:C1752")
...
Range(ActiveCell.Offset(0, 1) & ActiveCell.Row).Value = Application.WorksheetFunction.VLookup(Range(Activecell.Address), x, 2, 0)
The important bits are making sure your first two arguments are Ranges for the Vlookup function.
Related
I have an Excel file that consists of worksheets named 1, 2, 3, and so on. These worksheets involve data that I would like to analyze in the same ranges. I want to analyze these data in a single worksheet (Let's called it Master Worksheet).
So, to give an example, for the B2 cell of the Master worksheet, I would like to take average of the range B2:B11 of worksheet 1. For C2 cell, I would like to take the minimum of C2:C11 of worksheet 1, and so on.
Similarly, for B3 cell of the Master worksheet, I would like to take average of the range B2:B11 of worksheet 2. For C3 cell, I would like to take the minimum of C2:C11 of worksheet 2, and so on.
For this, I could not find any shortcuts in Excel, so I thought writing an VBA code would work. However I am not very familiar with it, so I am a bit stuck.
Sub MasterWorksheet()
Dim i as integer
Dim TotalNumberofWorksheets As Integer
TotalNumberofWorksheets = 99
For i to TotalNumberofWorksheets:
ActiveWorkbook.Worksheets("Master Worksheet").Cell(2, i+1).Value = ActiveWorkbook.Worksheets(i).Range("B2:B11").Average()
ActiveWorkbook.Worksheets("Master Worksheet").Cell(2, i+1).Value = ActiveWorkbook.Worksheets(i).Range("C2:C11").Min()
...
End Sub
I tried this, however it did not work. Any idea on how to write it?
The correct syntax for a For loop is
For i = 1 To TotalNumberofWorksheets 'you can make it start at i=2 too if need be
'or go backwards from TotalNumber and then use TotalNumberofWorksheets Step = -1
'your code here
Next i
So no need for : after the end-step and a needed i = startValue
I doubt you have 99 worksheets in your workbook, hence why I would change your code to
Sub MasterWorksheet()
Dim i As Integer
Dim TotalNumberofWorksheets As Integer
TotalNumberofWorksheets = ActiveWorkbook.Sheets.Count
For i = 1 To TotalNumberofWorksheets
If ActiveWorkbook.Worksheets(i).Name <> "Master Worksheet" Then
ActiveWorkbook.Worksheets("Master Worksheet").cell(2, i + 1).Value = ActiveWorkbook.Worksheets(i).Range("B2:B11").Average()
ActiveWorkbook.Worksheets("Master Worksheet").cell(2, i + 1).Value = ActiveWorkbook.Worksheets(i).Range("C2:C11").Min()
'etc
End If
Next i
End Sub
This way you also have a security to not include the master sheet.
I am using the below code to look up data in another workbook and collect the data. I have been able to get it to work in book 2.xlsm for my data entry sheet that is located in test.xlsx
Sub copydata()
Dim rw As Long, x As Range
Dim extwbk As Workbook, twb As Workbook
Set twb = ThisWorkbook
Set extwbk = Workbooks.Open("/Users/username/desktop/test.xlsx")
Set x = extwbk.Worksheets("Data entry").Range("A1:GZ400")
With twb.Sheets("Sheet1")
For rw = 4 To .Cells(Rows.Count, 1).End(xlUp).Row
.Cells(rw, 2) = Application.VLookup(.Cells(rw, 1).Value2, x, 11, False)
Next rw
End With
With twb.Sheets("sheet1")
For rw = 4 To .Cells(Rows.Count, 1).End(xlUp).Row
.Cells(rw, 3) = Application.VLookup(.Cells(rw, 1).Value2, x, 12, False)
Next rw
End With
extwbk.Close savechanges:=False
End Sub
What I want to be able to do is also get VBA to pull (I'm guessing using a combo of indirect and vlookup?) the integral data from the additional sheets in test.xlsx and place in book2 integral values. These sheet names in test.xlsx will change based on the sample name, but those names will be the same names that are in sheet1 of book2, same for the integral names.
Can someone help guide me to how I can add on to this code and address this? I am new to VBA so I am still learning. My actual documents are much larger and so I will need to tweak the reference cells in the end so please try to explain what some of the things mean so I know what I will be doing.
Note: working code at the end.
I'd say that the easiest way to act is to write a working formula for the range you want to fill. You can then start to record a macro of you typing the formula. With some edit, you can properly insert the resulting code in your subroutine.
STEP 1: writing the formula.
Since in your subroutine the test.xlsx will be open, your formula can be written and tested while test.xlsx is opened. You already have rightfully guessed the formula you need (VLOOKUP and INDIRECT). But for the sake of explanation, let's assume you've started with a simplier formula, like this one for the cell D4:
=VLOOKUP(D$3,'[test.xlsx]sample 1'!$D:$H,5,FALSE)
To make it dynamically choose the right sheet, we need to edit the table_array part. Within it, two parts are constant: '[test.xlsx] and '!$D:$H. They can be written as strings. The sample 1 is contained in the first cell of the row, so we will just write a reference to it. Our formula will therefore look like this:
=VLOOKUP(D$3,INDIRECT("'[test.xlsx]" & $A4 & "'!$D:$H"),5,FALSE)
Our formula is fairly functional. Let's record the macro.
STEP 2: recording and editing the macro.
Start the recording, select the cell with the formula, press F2, press enter, stop the recording. You can then go to VBA and there you'll find (presumably in a new module) the macro you've just recorded. It will most likely look like this:
Sub Macro1()
'
' Macro1 Macro
'
'
Range("D4").Select
ActiveCell.FormulaR1C1 = _
"=VLOOKUP(R3C,INDIRECT(""'[test.xlsx]"" & RC1 & ""'!$D:$H""),5,FALSE)"
Range("D5").Select
End Sub
Of all this code, what we really care about is the .FormulaR1C1 = "=VLOOKUP(R3C,INDIRECT(""'[test.xlsx]"" & RC1 & ""'!$D:$H""),5,FALSE)". As you can see, it changes the property FormulaR1C1 of the given range. Basically we can insert a string that (if correctly formatted) it will be read as a formula with RC (row-column) type references. More information about it here. While integrating this formula, we can also change the '[test.xlsx] part to use a reference depending on our code. Thefore we change this:
.FormulaR1C1 = "=VLOOKUP(R3C,INDIRECT(""'[test.xlsx]"" & RC1 & ""'!$D:$H""),5,FALSE)""
into this:
.FormulaR1C1 = "=VLOOKUP(R3C,INDIRECT(""'[" & extwbk.Name & "]"" & RC1 & ""'!$D:$H""),5,FALSE)"
This way in case extwbk had a different name, the resulting formula would still work. We could also do the same thing basically with all the variables in the formula.
Now we need to determine the formula's range of destination. We already have a range variable in our code that we can use. Mind that a single letter name for a variable is not the best. You should choose a name with at least 3 letters that you presumably won't find in the rest of the code. This will make easier the search and eventually the edit of the given variable. It's also a good practice to add a "tag" to the variable to underline what kind of variable it is (example Rng if it's a range, like RngMyCell). It can be also risky to use the same vaguely called variable for different purpouse in a code, but since it's a really short code we should be fine (and you can still improve the code accordingly later). Anyway, to determine the range we can use Resize and Offset starting from the cell D3 like this:
With twb.Sheets("Sheet1")
Set x = .Range(.Range("D3"), .Cells(3, .Columns.Count).End(xlToLeft))
Set x = x.Resize(.Cells(.Rows.Count, 1).End(xlUp).Row - 3, x.Columns.Count).Offset(1, 0)
End With
We can then apply our formula to the x range. Since we are interested in the formulas' results and not in the formulas themselves we can add a x.Values = x.Values line to sobstitute the formulas with their results. Our code will therefore be like this:
With twb.Sheets("Sheet1")
Set x = .Range(.Range("D3"), .Cells(3, .Columns.Count).End(xlToLeft))
Set x = x.Resize(.Cells(.Rows.Count, 1).End(xlUp).Row - 3, x.Columns.Count).Offset(1, 0)
x.FormulaR1C1 = "=VLOOKUP(R3C,INDIRECT(""'[" & extwbk.Name & "]"" & RC1 & ""'!$D:$H""),5,FALSE)"
x.Value = x.Value
End With
Our code is ready to be integrated into our subroutine.
STEP 3: integrating the code
We can place our code in our subroutine. But first we can also merge the two With twb.Sheets("Sheet1") since they are identical and put our code within the same with statement. We can also note that in our For-Next cycles a point is missing. The end result of should be like this:
Sub copydata()
Dim rw As Long, x As Range
Dim extwbk As Workbook, twb As Workbook
Set twb = ThisWorkbook
Set extwbk = Workbooks.Open("/Users/username/desktop/test.xlsx")
Set x = extwbk.Worksheets("Data entry").Range("A1:GZ400")
With twb.Sheets("Sheet1") '<-- this with is the same as the next one. No need to repeat it.
For rw = 4 To .Cells(.Rows.Count, 1).End(xlUp).Row '<-- the ".Rows.Count" didn't have the point.
.Cells(rw, 2) = Application.VLookup(.Cells(rw, 1).Value2, x, 11, False)
Next rw
For rw = 4 To .Cells(.Rows.Count, 1).End(xlUp).Row '<-- the ".Rows.Count" didn't have the point.
.Cells(rw, 3) = Application.VLookup(.Cells(rw, 1).Value2, x, 12, False)
Next rw
Set x = .Range(.Range("D3"), .Cells(3, .Columns.Count).End(xlToLeft))
Set x = x.Resize(.Cells(.Rows.Count, 1).End(xlUp).Row - 3, x.Columns.Count).Offset(1, 0)
x.FormulaR1C1 = "=VLOOKUP(R3C,INDIRECT(""'[" & extwbk.Name & "]"" & RC1 & ""'!$D:$H""),5,FALSE)"
x.Value = x.Value
End With
extwbk.Close savechanges:=False
End Sub
I'm trying to grab the values from a different worksheet and match them to the their sister data in my main sheet in column A but I'm having issues with getting the right results, I was thinking of going the Vlookup route but I can't quite get it to work properly. I found a funky way of getting it done but I'm trying to save just the values and not the formula itself.
This is what I tried at first
Sub matchID()
'Dim wb As Workbook
'Set wb = ActiveWorkbook
'
'With wb.Sheets("Data")
' .Range("E2:E" & .Range("A" & .Rows.Count).End(xlUp).Row).Formula = "=VLOOKUP(A2,ID!A:B,2,FALSE)"
'End With
'the above works but need to save values and not formula
It kinda works but I need that values and not the formula, my plan is to find the data I need and then save a copy of the file as a csv
I tried using a different method but I'm running into runtime error '1004'
I'm still learning VBA so I feel like I'm spinning my wheels right now.
Can someone show me what I'm doing wrong?
Sub matchID()
'this is what I'm trying to get to work but unsure if I will still end up with formula and not just values
Dim result As String
Dim sheet As Worksheet
Dim lrow As Integer
Dim i As Integer
Set sheet = ActiveWorkbook.Sheets("Data")
lrow = sheet.UsedRange.Rows(sheet.UsedRange.Rows.Count).Row
For i = 2 To lrow
result = Application.WorksheetFunction.VLookup("A2", Sheets("ID").Range("A:B"), 2, False)
Cells(i, 5).Value = result
Next
End Sub
I'm trying to lookup all IDs(in column B) from my second sheet("ID") using the values in column A from my primary sheet("Data") and then populate the all results in column E in my primary sheet to their match.
My first try kinda worked but instead of leaving just the value it leaves the formula in the cell e.g. =VLOOKUP(A2,ID!A:B,2,FALSE) when really I'm looking for just the value 8447 that it shows before clicking on the cell.
If you want to get rid of the formula, just paste as values:
Sub matchID()
Dim wb As Workbook
Set wb = ActiveWorkbook
With wb.Sheets("Data")
.Range("E2:E" & .Range("A" & .Rows.Count).End(xlUp).Row).Formula = "=VLOOKUP(A2,ID!A:B,2,FALSE)"
.Range("E2:E" & .Range("A" & .Rows.Count).End(xlUp).Row).Value = .Range("E2:E" & .Range("A" & .Rows.Count).End(xlUp).Row).Value
End With
End Sub
I need to remove the first half of a formula on multiple sheets throughout a workbook. Four specific cells per sheet (F308, F315, F322, F329) need to have just the first part of the formula removed.
The formula is: ='Project Input - John1'!$D$954*'Project Input - John1'!$D$952
The major catch here is each cells formula on each sheet, while extremely similar, is different. Each sheet referred to is different on each sheet and each referring cell is different in every cell.
I don't want to change the unique second part, just remove the first part: 'Project Input - John1'!$D$954*. or everything before (and including) *.
Is it possible to create a workbook formula to do this where I only have to run it once? If not, is it possible to do it sheet by sheet with a standard reference like, "This sheet"? Thanks!
Edit:
I have tried to record a macro but it applied the last part of the formula from the cell I recorded it in
I tried this VBA:
Sub test()
tx = Split(Cells(6, 315), "4")
For i = LBound(tx) To UBound(tx)
Cells(1, 2) = tx(i)
Next
End Sub
and this:
Dim ichar As Integer ichar = InStr(1, cl.Value, afterString, vbTextCompare)
cl = Left(cl.Value, ichar + Len(afterString) -1)
End Sub
Sub test() Call removetextbefore("*", Sheet11.Cell(f308))
End Sub
I got a compile error
I'm trying to find code that works and have scoured the sites, but nothing is seeming to work.
You may be able to do something like this, using the Split method:
Sub editFormulas()
Dim R As Range
Dim WS As Worksheet
For Each WS In ThisWorkbook.Worksheets
With WS
For Each R In Union(.Cells(308, 6), .Cells(315, 6), .Cells(322, 6), .Cells(329, 6))
R.Formula = "=" & Split(R.Formula, "*")(1)
Next R
End With
Next WS
End Sub
I receive an excel file monthly and have to export parts of it to a new file. I have a list of identifier numbers and I am trying to match the list of numbers in the selected list to the full file and then export the rows of relevant data to a new sheet.
Sub Run_All_Macros()
Application.ScreenUpdating = False
Sheets.Add.Name = "Output"
Call Convert_to_Numbers
Call Highlight_Selected_Contractors
End Sub
'Original Spreadsheet is formatted incorrectly
'Convert PSD Codes to Numbers
Sub Convert_to_Numbers()
Dim xCell As Range
Range("A2:A2500").Select
For Each xCell In Selection
xCell.Value = CDec(xCell.Value)
Next xCell
End Sub
'Highlight Selected Contractors
Sub Highlight_Selected_Contractors()
Dim Full, Selection, Code, SelectedCode As Range
Worksheets("Sheet1").Select
'Set all cells in Column A Sheet 1 to Full
Set Full = Worksheets("Sheet1").Range("A1", Range("A1").End(xlDown))
'Set all cells in Column A Sheet 2 to Selection
Worksheets("Sheet2").Select
Set Selection = Worksheets("Sheet2").Range("A1", Range("A1").End(xlDown))
'If the numbers match highlight the cell
For Each Code In Full
For Each SelectedCode In Selection
If Code.Value = SelectedCode.Value Then
*** Code.Select
Selection.Copy
Sheets.Select ("Output")
ActiveSheet.Paste
End If
Next SelectedCode
Next Code
End Sub
After executing this code column A in 'Output' is filled with zeros from A2:A2500. From messing around with breakpoints I've identified the problem to be where I've placed * but I'm not sure what's wrong with what's written there.
Thanks
There few errors in the code above and I also have few suggestions and finally the code.
ERRORS
1) Sheets.Add.Name = "Output" This line will give you an error if there is already a sheet called "Ouput". Delete the sheet first and then create it. You must be wondering that in case the sheet is not there, then how can I delete it? For such scenarios you can use On Error Resume Next which should be avoided in most cases.
2) When working with ranges, always specify which sheet you are referring to else Excel will always assume that you are referring to the "ActiveSheet". As you realized that Sub Convert_to_Numbers() was taking Output Sheet into consideration whereas you want the operation to happen in "Output" Sheet.
3) Dim Full, Selection, Code, SelectedCode As Range As mentioned in my comments earlier avoid using Excel Reserved words as variables. Also unlike VB.Net, if you declare variables as you did in VBA then only the last variable will be declared as Range. The other 3 will be declared as variant. VB defaults the variable to being type Variant. A Variant type variable can hold any kind of data from strings, to integers, to long integers, to dates, to currency etc. By default “Variants” are the “slowest” type of variables. Variants should also be avoided as they are responsible for causing possible “Type Mismatch Errors”. It’s not that we should never use Variants. They should only be used if you are unsure what they might hold on code execution.
4) Avoid the use of words like .ActiveCell, Selection, Select, Activate etc. They are a major cause of errors. Also they slow your code down.
SUGGESTIONS
1) Instead to using Sheets("WhatEver") every time, store it in a variable and then use that variable. Will cut down your code.
2) Indent your code :) it's much easier to read
3) Group tasks together. For example if you have to do with something with a particular sheet then keep it together. It is easier to read and amend if required.
4) Instead of hard coding your values, get actual ranges. Range("A2:A2500") is a classic example. Will you always have data till 2500? What if it is less or more?
5) End(xlDown) will never give you the last row if there is a blank cell in between. To get the last row in a column, say A in "Sheet1", use this
Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row`
6) Instead of looping, you can use the WorksheetFunction CountIf(). Loops should be avoided as much as possible as they slow down your code.
7) Use appropriate Error handling.
8) Comment your code. It's much easier to know what a particular code or section is doing.
CODE
Option Explicit
Sub Run_All_Macros()
Dim ws1I As Worksheet, ws2I As Worksheet, wsO As Worksheet
Dim ws1LRow As Long, ws2LRow As Long, wsOLr As Long
Dim xCell As Range, rFull As Range, rSelection As Range
Dim rCode As Range, rSelectedCode As Range
On Error GoTo Whoa '<~~ Error Handling
Application.ScreenUpdating = False
'~~> Creating the Output Sheet
Application.DisplayAlerts = False
On Error Resume Next
Sheets("Output").Delete
On Error GoTo 0
Sheets.Add.Name = "Output"
Application.DisplayAlerts = True
'~~> Working with 1st Input Sheet
Set ws1I = Sheets("Sheet1")
With ws1I
'~~> Get Last Row of Col A
ws1LRow = .Range("A" & Rows.Count).End(xlUp).Row
'~~> Set the range we want to work with
Set rFull = .Range("A1:A" & ws1LRow)
'~~> The following is not required unless you want to just format the sheet
'~~> This will have no impact on the comparision. If you want you can
'~~> uncomment it
'For Each xCell In .Range("A2:A" & ws1LRow)
'xCell.Value = CDec(xCell.Value)
'Next xCell
End With
'~~> Working with 2nd Input Sheet
Set ws2I = Sheets("Sheet2") '<~~ Input Sheet 2
ws2LRow = ws2I.Range("A" & Rows.Count).End(xlUp).Row
Set rSelection = ws2I.Range("A1:A" & ws2LRow)
'~~> Working with Output Sheet
Set wsO = Sheets("Output")
wsO.Range("A1") = "Common values"
wsOLr = wsO.Range("A" & Rows.Count).End(xlUp).Row + 1
'~~> Comparison : If the numbers match copy them to Output Sheet
For Each rCode In rFull
If Application.WorksheetFunction.CountIf(rSelection, rCode.Value) > 0 Then
rCode.Copy wsO.Range("A" & wsOLr)
wsOLr = wsOLr + 1
End If
Next rCode
MsgBox "Done"
LetsContinue:
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
Let me know if you still get any errors :)
HTH