Excel VBA Vlookup with Ranges - excel

I have vlookup formula that takes the value of A2 and returns the corresponding match found in my Lookup_Table in cell O2, this lookup continues for the rows underneath.
How would I modify this code to lookup a range of values in A:M, with the results placed in O:AA? Or do I have to manually code each column separately?
With Sheets("Example")
.Range("O2:O" & .Range("A" & Rows.Count).End(xlUp).Row).Formula = _
"=IF(ISERROR(VLOOKUP(A2,'Lookup_Table'!A:H,4,FALSE)),0,VLOOKUP(A2,'Lookup_Table'!A:H,4,FALSE))"
.Range("O2:O" & .Range("A" & Rows.Count).End(xlUp).Row).Value = _
.Range("O2:O" & .Range("A" & Rows.Count).End(xlUp).Row).Value 'Comment out if you'd like to leave the above formula in place
End With

Assuming Lookup_Table = 'Lookup_Table'!A:H, you could try something like:
With worksheets("Cross_Walk") ' Assumes Activeworkbook
.Range("E2:H" & .Range("A" & .Rows.Count).End(xlUp).Row).Formula = _
"=Iferror(VLOOKUP(A2,'Lookup_Table'!$A:$H,4,FALSE),0)"
End With
We assign the formula to range E2:H? where ? is whatever the last row is determined to be.
Excel observes relative and absolute references when assigning the same formula to a range of cells.
So since A2 in the VLOOKUP has a relative row and column reference (no $ signs), it will change to B2 when the formula is entered in F2 -- and so forth for the remaining columns and rows.
Also, if you're going to test whether the result of the VLOOKUP is an error, and then conditionally assign either zero or the matching value, you may as well just use IFERROR in your formula -- rather than performing the VLOOKUP twice.

Related

Using IFERROR with VLOOKUP

I am adding a formula into a spreadsheet via VBA that uses iferror and vlookup. Below is the code from the VBA
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
Range("O2:o" & LastRow).FormulaR1C1 = "=IFERROR(VLOOKUP(RC1,'Controls'!a:b,2,FALSE),""Missing"")"
But when I look back into the spreadsheet, I find the following formula in the cells
=IFERROR(VLOOKUP($A2,Controls!A:(B),2,FALSE),"Missing")
which always results in a value of "Missing". When I edited the formula in the sheet and removed the parens from the B in the lookup range, the formula resolved correctly by finding the value in the Controls sheet.
I've tried changing the lookup range from A:B to A1:B500, $A$1:$B$500, and $A:$B. I also tried breaking it up and using various concatenations.
My question is ... How can I get VBA to NOT add the parens around the B in my lookup range.
Your problem comes from the fact that you put a regular address (A:B) into a R1C1-Formula. Even if your lookup-range is in another sheet, Excel expect the address in R1C1-notation. Your formula would look like
dim formula as string
formula = "=IFERROR(VLOOKUP(RC1,Controls!C[-14]:C[-13],2,FALSE),""Missing"")"
Range("O2:o" & LastRow).FormulaR1C1 = formula
as this is super ugly, I would suggest you define a name for the range Controls!A:B. Then you can change the formula simply to
formula = "=IFERROR(VLOOKUP(RC1,MyControls,FALSE),""Missing"")"
I think you would need to use .Formula(...) instead of .FormulaR1C1(...) and use uppercase on your string like ...A:B...
Else, you could do the following:
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
Range("O2:o" & LastRow).FormulaR1C1 = "=IFERROR(VLOOKUP(RC1,'Controls'!a:b,2,FALSE),""Missing"")"
Dim cell As Range
For Each cell in Range("O2:O" & LastRow)
cell.Formula = Replace(cell.Formula, "(B)","B")
Next cell

Find/Replace cells in selected range having zero values (Formula cells) using VBA

How can i use "Find/Replace" (Or any other alternative if available except for loops) in VBA to replace cells in selected range with "0" to "1".
Note: All cells in selected range are formula based, their resultant value will be "0".
Thanks,
Prasanna M
Post Your Comments: Without Loops
Using Evaluate over the Range. As Per Here.
for unwrapped values:
Sub Test2()
Dim rngData As Range
Set rngData = ThisWorkbook.Worksheets("Sheet1").Range("B1:G650000")
rngData = Evaluate("Substitute(" & rngData.Address & "," & "0" & ", " & "1" & ")")
End Sub
for wrapped values:
Sub Test3()
Dim rngData As Range
Set rngData = ThisWorkbook.Worksheets("Sheet1").Range("B1:G650000")
rngData = Evaluate("Substitute(" & rngData.Address & "," & "Char(34)&" & "0" & "& Char(34)" & ", " & "Char(34) &" & "1" & "& Char(34)" & ")")
End Sub
I seems to work very well. I ran over 650000 rows, B to G, in a under 3 Seconds first time, under 10 seconds the 2nd time (i have other things running as well). 3rd, 4th & 5th time entire range was =CHAR(34)&RANDBETWEEN(0,1)&CHAR(34), 4th and 4th Generated random 0,1's over the range with formula, 5th was a choose randbetwen function and lookup, 6th a lookup against a table in another sheet. It Always did the find-replace in under 15 secs. I do not think the formulas you have in your sheet matter to the macro. Its looking at values only, and changing values. Its the depth and length of your range that matters to it more.
I note also; going by your previous question, you could build in your conditions in there: to change the 0's to 1's , only if/when the values in range A are 'Def' as opposed to 'abc', so it all runs from the vba sub (but you will loose your formulae unless you build those in that too).
3rd Proposed alternative soln:
Assign unique serial numbers or id's by row to the entire table, filter DEF by column A, copy/strip those Def rows out to Sheet 2, find-replace 0 to 1 there , cut/delete original Defs rows, paste sheet2's Defs in sheet 1. Sort by id. Job done. 1 min that would take. ok, maybe 2.

How to create a new incremental reference number in the next row in excel vba

I have inherited an Excel register which has the following VBA module. It is supposed to create a new reference based on the one in the row above and also copy the formatting down. In reality all it does is copy exactly what is in the row above.
Dim lr As Long
lr = Range("A" & Rows.Count).End(xlUp).Row
Range("A" & lr).AutoFill Destination:=Range("A" & lr).Resize(2)
Rows(lr).Offset(0, 0).Copy
Rows(lr).Offset(1, 0).EntireRow.PasteSpecial Paste:=xlPasteFormats
Application.CutCopyMode = False
I am new to VBA and I don't recognise any of this, can anyone tell me what it is trying to do or how I change it to what I need.
Let's try and break this down:
Rows
returns a collection of Range objects on the active worksheet; each Range object represents a single row.
Rows.Count
returns the number of possible rows on a worksheet. This number depends on which version of Excel you are using; in my case it is 1048576.
"A" & Rows.Count
returns the address of the last cell in column A, or A1048576
Range("A" & Rows.Count)
returns a Range object representing the last cell in column A.
Calling the End method:
Range("A" & Rows.Count).End(xlUp)
returns a new Range object whose end has been moved up from the end of the previous range. In other words, the new Range object represents the last used cell in column A.
Finally, we want to get the row number of the last used cell, using the Row property; and we'll store this number in the lr variable.
lr = Range("A" & Rows.Count).End(xlUp).Row
Let's look at the next line:
Range("A" & lr)
returns a Range object representing the cell at column A and the row number at lr, or the last used cell in column A.
We want to call the AutoFill method, to fill the values from a previous set of cells into a new set of cells. In this case, we want to auto-fill from the cell in the previous row to the cell in the next row. We can do that by passing a Range object representing the last used cell together with the new cell into the Destination parameter.
We can get such a Range by using the Resize method, passing in 2 as the number of rows in the new Range:
Range("A" & lr).Resize(2)
And calling the AutoFill method:
Range("A" & lr).AutoFill Destination:=Range("A" & lr).Resize(2)
But note that there are different ways to auto-fill: we could auto-fill the same values as in the previous cells. In order to specify that we want to treat the previous values as a series to generate new values, we need to pass in the autofill type:
Range("A" & lr).AutoFill Destination:=Range("A" & lr).Resize(2), Type:=xlFillSeries
VBA references
& operator
Excel object model references
Note that Rows and Range are members of the undocumented Global object, but they appear to work the same way as the corresponding properties on the Range object.
Range object
Rows and Count properties
Range and End properties
XlDirection.xlUp enumeration constant
Row property
Resize property
AutoFill method
XlAutoFillType.xlFillSeries enumeration constant
A guess, but I would specify the Type parameter of Range.AutoFill:
Range("A" & lr).AutoFill Destination:=Range("A" & lr).Resize(2), Type:=xlFillSeries

Why is my VBA code applying formula to all rows under a column?

I want to apply a formula only to rows that satisfy a condition, but it is somehow applying the formula to all rows.
I have an Excel sheet with a table in it. For column Q, I want to add a formula only if column K's value is NOT 0, and column I's value is 0.
But instead of applying it to rows that met the condition, it is applying the formula to all rows under column Q.
I added if-statement, and it seems to be working when I run the program in debug mode, and check if the correct rows are going into the if condition.
What is weird is, if I try to put some random string into the rows for "else", it works! But I want nothing in those rows that don't satisfy the condition.
Dim lRow, i As Integer
lRow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lRow 'Starts from i=2 because i=1 is the headers
If Range("K" & i).Value <> 0 And Range("I" & i).Value = 0 Then
Range("Q" & i).FormulaR1C1 = "=RC[-6]" 'This is the formula.
Else
Range("Q" & i).Value = "" 'Doesn't work.
Range("Q" & i).Value = "XXX" 'Works, but it inserts "XXX" to the cells. Ideally, I dont want anything, any formula in the cells.
End If
Next i
I expect that the rows that satisfy the condition should get the formula inserted under column Q, but I don't see why my code doesn't work.
As GSerg points out in the comments this behaviour is due to Excel autofilling the formula as it is in a table.
You can insert the following into your code which will turn off the autofill:
Application.AutoCorrect.AutoFillFormulasInLists = False
Remember to set it back to True again if it is desired behaviour for you normally (or if you are sharing this macro with others).

Find Maximum Value for a column in different sheets and report it in result sheet

I want to check Column A (A1:A365) in Sheet2, Sheet3, Sheet4 and Sheet5 and find Maximum value for each cell. Compare A1 in Sheet2, Sheet3, Sheet4 and Sheet5, find maximum of it and report it in A1 in result page. Also in cell B1 report corresponding sheet for this maximum. This goes on to Column A (A1:A365)
the following code i used:
Worksheets("sheet2").Range("A1").Value = a
Worksheets("sheet3").Range("A1").Value = b
Worksheets("sheet4").Range("A1").Value = c
Worksheets("sheet5").Range("A1").Value = d
MaxValue = Application.Max(a, b, c, d)
Range("A1").Value = MaxValue
yes i have just 4 sheets – Mohsen 11 mins ago
Non VBA Solution
In Sheet1, Cell A1, put this formula
=MAX(Sheet2!A1,Sheet3!A1,Sheet4!A1,Sheet5!A1)
In Sheet1, Cell B1, put this horrifying formula. I am sure there is a better way to find the sheet name though.
=IF(Sheet1!A1=Sheet2!A1,RIGHT(CELL("filename",Sheet2!A1),LEN(CELL("filename",Sheet2!A1))- FIND("]",CELL("filename",Sheet2!A1),1)),IF(Sheet1!A1=Sheet3!A1,RIGHT(CELL("filename",Sheet3!A1),LEN(CELL("filename",Sheet3!A1))- FIND("]",CELL("filename",Sheet3!A1),1)),IF(Sheet1!A1=Sheet4!A1,RIGHT(CELL("filename",Sheet4!A1),LEN(CELL("filename",Sheet4!A1))- FIND("]",CELL("filename",Sheet4!A1),1)),IF(Sheet1!A1=Sheet5!A1,RIGHT(CELL("filename",Sheet5!A1),LEN(CELL("filename",Sheet5!A1))- FIND("]",CELL("filename",Sheet5!A1),1)),""))))
A word of caution though. To use the RIGHT(CELL("filename",Sheet2!A1),LEN(CELL("filename",Sheet2!A1))- FIND("]",CELL("filename",Sheet2!A1),1)), you need to have the workbook saved.
My Sheet2, A1 has 1, Sheet3, A1 has 2, Sheet4, A1 has 2.5,Sheet5, A1 has 3
VBA Solution
Sub Sample()
Dim ws As Worksheet
Dim i As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
.Range("A1:A365").Formula = "=MAX(Sheet2!A1,Sheet3!A1,Sheet4!A1,Sheet5!A1)"
.Range("A1:A365").Value = .Range("A1:A365").Value
For i = 1 To 365
Select Case .Range("A" & i)
Case ThisWorkbook.Sheets("Sheet2").Range("A" & i).Value: .Range("B" & i).Value = "Sheet2"
Case ThisWorkbook.Sheets("Sheet3").Range("A" & i).Value: .Range("B" & i).Value = "Sheet3"
Case ThisWorkbook.Sheets("Sheet4").Range("A" & i).Value: .Range("B" & i).Value = "Sheet4"
Case ThisWorkbook.Sheets("Sheet5").Range("A" & i).Value: .Range("B" & i).Value = "Sheet5"
End Select
Next i
End With
End Sub
Quick solution
Based on Sidd's answer, here's my non-VBA solution without the horrifying formula:
Place this formula in Sheet1!A1: =MAX(Sheet2:Sheet5!A1)
As the sheet name does not have to be flexible (I'd assume you don't change it that often), you can use this formula in B1:
=IF(Sheet2!A1=A1,"Sheet2",
IF(Sheet3!A1=A1,"Sheet3",
IF(Sheet4!A1=A1,"Sheet5",
"Sheet5")))
More structural solution (better suited for many worksheets):
If you have many worksheets, you could consider this alternative.
Have a list of the relevant worksheets stored somewhere in your worksheet. (in the example, I place the list in E3:E7). Name this range Sheets. (Similar to Sidd's horrifying formula, I used the CELL formula to dynamically get each sheet name. However, this is not necessary in a static model)
(same as step 1 above): Place this formula in Sheet1!A1: =MAX(Sheet2:Sheet5!A1)
Place this formula in A2:
=INDEX(Sheets,MATCH(1,COUNTIF(INDIRECT("'"&Sheets&"'!A1"),A1),0))
Enter it as an array formula, i.e. press Ctrl-Shift-Enter instead of Enter.
I uploaded the second solution here.
Kudos to this instruction!
Since the formula is intended to be copied down, you don't want to hard-code a reference to cell A1 in the INDIRECT in Peter Albert's formula. You can use CELL("address",A1) to get a reference to A1 that can be copied down instead.
You can also avoid the need to array-enter the formula by using LOOKUP to return the result instead of INDEX & MATCH.
Note that LOOKUP will return the name of the last worksheet with the max value in case of a tie.
The following formulas use a named range Sheets with the names of each worksheet
=MAX(Sheet2:Sheet5!A1) returns max value (identical to Peter Albert's formula)
=LOOKUP(2,1/COUNTIF(INDIRECT("'" & Sheets & "'!" & CELL("address",A1)),A1),Sheets) returns name of sheet with max value
Make two new sheets with one titled "First" and the other "Last". Make new sheets for your project originate through a button that contains a macro to add duplicate sheets between "First and Last". Then just put one of these simple formulas for example in your reporting cell: =SUM(First:Last!K28) or =MAX(First:Last!K28)

Resources