Runtime Error 5, invalid procedure or call argument - vba

I am trying to assign Cell E8 in Sheet"Report" with an Index Match formula with a dynamic range. The range is from Sheet"Data"
I have found the last row (LR) and last column (lc).
The run time error occurs at the last line: Cell("E8").formula = "=...."
This is the code:
Sub report()
Dim LR As Long, lc As Long, first As Long, proxy As String
Sheets("Data").Select
'Finding the first filled cell by moving down from A1
first = Sheets("Data").Range("A1").End(xlDown).Row
'The first row has column headers: Name, ID number, etc... SO I assign it to the next row where the first data entry is
first = first + 1
LR = Sheets("Data").Range("A" & first).End(xlDown).Row
lc = Sheets("Data").Range("A" & first).End(xlToRight).Column
Sheets("Report").Select
proxy = "=IFERROR(INDEX(Data!$A$10:" & Cells(LR, lc).Address & ",MATCH(Report!$C$3,Data!$A$10:" & Cells(LR, 1).Address & ",0),MATCH(Report!$C8,Data!A$9:" & Cells(9, lc).Address & ",0)),'N/A')"
Cells("E8").Formula = proxy
End Sub

Sub report()
Dim LR As Long, lc As Long, first As Long, proxy As String
With Sheets("Data")
'Finding the first filled cell by moving down from A1
first = .Range("A1").End(xlDown).Row
'The first row has column headers: Name, ID number, etc... SO I assign it to the next row where the first data entry is
first = first + 1
LR = .Range("A" & Rows.Count).End(xlUp).Row
lc = .Range("A" & first - 1).End(xlToRight).Column
End With
proxy = "=IFERROR(INDEX(Data!$A$10:" & Cells(LR, lc).Address & ",MATCH(Report!$C$3,Data!$A$10:" & Cells(LR, 1).Address & ",0),MATCH(Report!$C8,Data!A$9:" & Cells(9, lc).Address & ",0)),""N/A"")"
Sheets("Report").Range("E8").Formula = proxy
End Sub

You are using single quotes to wrap 'N/A'. You need double quotes and because they are quotes within a quoted string, you need to double them up. Additionally, the Range.Cells property does not accept the same style of cell address references that a Range object does.
proxy = "=IFERROR(INDEX(Data!$A$10:" & Cells(LR, lc).Address & _
",MATCH(Report!$C$3,Data!$A$10:" & Cells(LR, 1).Address & _
",0),MATCH(Report!$C8,Data!A$9:" & Cells(9, lc).Address & _
",0)),""N/A"")"
Sheets("Report").Select
Range("E8").Formula = proxy
Here is a quick rewrite that gets away from using Worksheet.Select¹ method and the implicit ActiveSheet property.
Sub report()
Dim lr As Long, lc As Long, first As Long, proxy As String
With Worksheets("Data")
'Finding the first filled cell by moving down from A1
first = .Range("A1").End(xlDown).Row
'The first row has column headers: Name, ID number, etc... SO I assign it to the next row where the first data entry is
first = first + 1
lr = .Range("A" & first).End(xlDown).Row
lc = .Range("A" & first).End(xlToRight).Column
End With
proxy = "=IFERROR(INDEX(Data!$A$10:" & Cells(lr, lc).Address & _
",MATCH(Report!$C$3,Data!$A$10:" & Cells(lr, 1).Address & _
",0),MATCH(Report!$C8,Data!A$9:" & Cells(9, lc).Address & _
",0)),""NA"")"
With Worksheets("Report")
.Range("E8").Formula = proxy
'alternate with .Cells as one of these
'.Cells(8, "E").Formula = proxy
'.Cells(8, 5).Formula = proxy
End With
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.

Related

Excel VBA - Evaluate Function Returning #NAME? Error

So I wrote a VBA code to calculate the number of blanks, non-blanks and total entries under each header for some input data. I want to add a code that copies and pastes the values from one sheet to another, dedupes the values, gives me the unique list of values under each header, number of unique values, and the number of times those unique values are occurring under the header.
Blanks: I used the countblank function earlier, but it would skip certain empty fields, so I changed it to sumproduct(len(Range)=0)*1).
Non-Blanks: I wrote a similar function and tried to calculate the above.
It turns out VBA is unable to process the Sumproduct function. Here are the approaches I have tried:
1. Application.WorksheetFunction.Sumproduct(...)
2. ..Number.. = "=Sumproduct(...)"
3. ..Number.. = Evaluate("Sumproduct(...)")
4. ..Number.. = Worksheet.Evaluate("Sumproduct(...)")
Below is the code for the macro, I am writing the code on the Input_File, i.e., the Input worksheet.
Sub Dedupe()
ThisWorkbook.Worksheets("Control_Totals").Cells.ClearContents
Dim lRow As Long
Dim lCol As Long
Dim i As Long
Dim j As Long
Dim Input_File As Worksheet
Dim Output_File As Worksheet
Dim Dedup_File As Worksheet
Dim Col_Let As String
Dim Rng As String
Dim blank As String
Dim non_blank As String
Set Input_File = ThisWorkbook.Worksheets("Input")
Set Output_File = ThisWorkbook.Worksheets("Control_Totals")
Set Dedup_File = ThisWorkbook.Worksheets("Deduped")
With Output_File
.Cells(1, 1) = "Field_Name"
.Cells(1, 2) = "Blanks"
.Cells(1, 3) = "Non-Blanks"
.Cells(1, 4) = "Total"
End With
'Finding the last row among all entries, including the blank ones
lRow = Input_File.Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
MsgBox "Last Row: " & lRow
'Finding the last column header/field
lCol = Input_File.Cells.Find(What:="*", _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
MsgBox "Last Column: " & lCol
i = 1
'Finding the number of blank and non-blank entries for all the fields
Do While i < lCol + 1
Col_Let = ColumnLetter(i)
Rng = "Input!" & "Col_Let" & "2" & ":" & "lRow"
Output_File.Cells(i + 1, 1) = Input_File.Cells(1, i)
blank = "SumProduct((Len(Rng) = 0) * 1)"
non_blank = "SumProduct((Len(Rng) > 0) * 1)"
Output_File.Cells(i + 1, 2).Value = Evaluate(blank)
Output_File.Cells(i + 1, 3).Value = Evaluate(non_blank)
Output_File.Cells(i + 1, 4) = lRow - 1
'Deduping the data under the headers
j = 0
For j = 1 To lRow
Dedup_File.Cells(j, i).Value = Input_File.Cells(j, i).Value
j = j + 1
Next
Dedup_File.Range(Cells(1, i), Cells(lRow, i)).RemoveDuplicates Columns:=1, _
Header:=xlYes
i = i + 1
Loop
End Sub
These lines don't do what you think they do
Col_Let = ColumnLetter(i)
Rng = "Input!" & "Col_Let" & "2" & ":" & "lRow"
Rng is always a string containing "Input!Col_Let2:lRow"
What you meant was: (I think)
Rng = "Input!" & Col_Let & "2" & ":" & Col_Let & lRow
Secondly Rng exists only within this vba routine - it doesn't mean anything to Excel so you can't use it in an Excel Formula. You need
blank = "SumProduct((Len(" & Rng.address & ") = 0) * 1)"
and finally SumProduct doesn't like those sort of tricks in VBA (It relies on excel expanding the 1 into an array automatically). A better solution:
Dim cBlank as long
Dim cNonBlank as long
Dim r as range
For each r in rng
if r.text = "" then
cBlank= cBlank+1
else
cNonBlank = cNonBlank +1
end if
next r
I want to add a code that copies and pastes the values from one sheet to another, dedupes the values, gives me the unique list of values under each header, number of unique values, and the number of times those unique values are occurring under the header.
What you have just described there is a PivotTable, with the field of interest in both the Rows area and in the Values area as a Count.

How to set the Match range as a variable in VBA?

Problem: I'm trying to run a match function based on a range that has variables.
Scope: The goal is to find the first instance of the word "awesome" in column A between row x and the last row; and return the row number
Case:
LastRow = Sheets("demo").Cells(Rows.Count, 1).End(xlUp).Row
For x = 2 To LastRow
Dim rng as Range
Set rng = "A" & x & ":A" & LastRow
Attempts:
"=Match("awesome","A" & x & ":A" & LastRow,0)"
"=Match("awesome",range(""A"" & x & "":A"" & LastRow),0)"
"=Match("awesome",rng,0)"
"=Match("awesome",range(rng),0)"
Nothing Works!
This code will return the row number. The lookat:=xlWhole matchs the entire cell value, lookat:=xlPart, will match any part of the cell value
firstrowmatch = Worksheets("Demo").Range("A" & x & ":A" & LRow).Find(what:="awesome", lookat:=xlWhole).Row

Converting formula to VBA

I am very new to VBA, I have a formula with me, Which I want in Macro.
I used Macro recording to have the vba, but the Problem is it is generating for each row, making the vba code complex. also, it Displays the formula in formula bar.
I have two Sheets , sheet 1 (BW) and sheet2(CW). I want the code in sheet 1. it Looks for ID in sheet 2 and copy the values from Ad to Au of sheet1.
I have tried, to some extent to implement my formula to code.
=IF(IFERROR(VLOOKUP($B2;CW!$B$2:$AU591;30;FALSE);"0")=0;" ";IFERROR(VLOOKUP($B2;CW!$B$2:$AU591;30;FALSE);""))
Sub lookupo()
Dim totalrows As Long
Dim totalrowssh2 As Long
totalrows = Sheets("BW").Cells(Rows.Count, "A").End(xlUp).Row
totalrowsSht2 = Sheets("CW").Cells(Rows.Count, "A").End(xlUp).Row
sheets("BW").Range("AD2:AD" & Totalrows).formula = Application.WorksheetFunction.If(Iferror(Apllication.Vlookup(sheets("BW").Range("B2:B" &totalrowssht2), Sheets("CW").Range("$A:$AU"),29,False),"0"))=0,"")
End Sub
I am struck how i should implement the second formula in line. Could someone help me to overcome with a VBA code.
No need to use Application.WorksheetFunction. You can directly assign the formula string to a cell.
Is this what you are trying?
Sub lookupo()
Dim BWlRow As Long, CWlRow As Long
Dim Sformula As String
Dim wsBW As Worksheet, wsCW As Worksheet
Set wsBW = Sheets("BW"): Set wsCW = Sheets("CW")
BWlRow = wsBW.Cells(wsBW.Rows.Count, "A").End(xlUp).Row
CWlRow = wsCW.Cells(wsCW.Rows.Count, "A").End(xlUp).Row
Sformula = "=IF(IFERROR(VLOOKUP($B2;CW!$B$2:$AU" & _
CWlRow & _
";30;FALSE);""0"")=0;"" "";IFERROR(VLOOKUP($B2;CW!$B$2:$AU" & _
CWlRow & _
";30;FALSE);""""))"
wsBW.Range("AD2:AD" & BWlRow).Formula = Sformula
End Sub
Use this if ; is not your separator.
Sformula = "=IF(IFERROR(VLOOKUP($B2,CW!$B$2:$AU" & _
CWlRow & _
",30,FALSE),""0"")=0,"" "",IFERROR(VLOOKUP($B2,CW!$B$2:$AU" & _
CWlRow & _
",30,FALSE),""""))"
How can i extend the same till column AU. ? – Mikz 4 mins ago
You will have to loop through the columns and amend the formula before applying it.
Sub lookupo()
Dim BWlRow As Long, CWlRow As Long, i As Long
Dim Sformula As String
Dim wsBW As Worksheet, wsCW As Worksheet
Set wsBW = Sheets("BW"): Set wsCW = Sheets("CW")
BWlRow = wsBW.Cells(wsBW.Rows.Count, "A").End(xlUp).Row
CWlRow = wsCW.Cells(wsCW.Rows.Count, "A").End(xlUp).Row
For i = 30 To 47 '~~> Col AD to AU
Sformula = "=IF(IFERROR(VLOOKUP($B2,CW!$B$2:$AU" & _
CWlRow & _
"," & _
i & _
",FALSE),""0"")=0,"" "",IFERROR(VLOOKUP($B2,CW!$B$2:$AU" & _
CWlRow & _
"," & _
i & _
",FALSE),""""))"
With wsBW
.Range(.Cells(2, i), .Cells(BWlRow, i)).Formula = Sformula
End With
Next i
End Sub
Application.WorksheetFunction is a good idea, if you think that one day your Excel would be used outside an English speaking country. Furthermore, you should not be worrying about the formula separators this way, Excel sets them automatically.
Having said that, try to use Option Explicit at the top of your file (this highlights variable definition errors immediately) and then correct your code and then fix a bit of it like this:
totalrowssh2 - make sure that totalrowssht2 is the same
everywhere.
Apllication.Vlookup - take a look here and fix the grammer mistake.
Then try the code below and fix it a bit:
Sub lookupo()
Dim totalrows As Long
Dim totalrowssh2 As Long
totalrows = Worksheets("BW").Cells(Rows.Count, "A").End(xlUp).Row
totalrowssh2 = Worksheets("CW").Cells(Rows.Count, "A").End(xlUp).Row
Worksheets(1).Range("AD2:AD" & totalrows).Formula = Application.WorksheetFunction.If(WorksheetFunction.IfError(Application.VLookup(Sheets(1).Range("B2:B" & totalrowssh2), Sheets("CW").Range("$A:$AU"), 29, False), "0") = 0, "")
End Sub

VBA Excel - Select rows with Left value = to variable

Having trouble with this code. No errors but also doesn't seem to do anything.
In my sheet, column "M" has some values that start with the letter "T" I want to select the entire row for these. Thanks in advance.
Sub trace1()
Dim trace As String
trace = "T"
Dim LR As Long, i As Long
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If Left(Range("M" & i).Value, 1) = trace Then Rows("i:i").Select
Next i
End Sub
One possible way to answer the question as written:
Sub trace1()
Dim trace As String
trace = "T"
Dim LR As Long, i As Long
Dim SelectedRows As Range
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If Not SelectedRows Is Nothing Then
If Left(Range("M" & i).Value, 1) = trace Then Set SelectedRows = Union(SelectedRows, Rows(i))
Else
If Left(Range("M" & i).Value, 1) = trace Then Set SelectedRows = Rows(i)
End If
Next i
SelectedRows.Select 'Replace with .Copy if that's what you really wanted.
End Sub
If you are trying to select the row assigned to variable "i", you would use:
Sub trace1()
Dim trace As String
trace = "T"
Dim LR As Long, i As Long
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If Left(Range("M" & i).Value, 1) = trace Then Rows(i).Select
Next i
End Sub
"Rows("i:i")" won't work. Try collecting all the addresses of all ranges in one string and then selecting the string. Note the comma which separates each range.
Sub trace1()
Dim sRange As String
Dim trace As String
trace = "T"
Dim LR As Long, i As Long
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If Left(Range("M" & i).Value, 1) = trace Then sRange = sRange & "," & i & ":" & i
Next i
Range(Mid(sRange, 2)).Select
End Sub
using AutoFilter to give you the range as is, or the actual address
avoids slow loops
Sub trace2()
Dim strTrace As String
Dim strAddress
Dim rng1 As Range
strTrace = "T"
Set rng1 = Range([m1], Cells(Rows.Count, "M").End(xlUp))
With rng1
.AutoFilter 1, strTrace & "*"
Set rng1 = rng1.Cells(1).Offset(1, 0).Resize(rng1.Rows.Count - 1, 1)
strAddress = rng1.SpecialCells(xlVisible).EntireRow.Address
End With
MsgBox "rows that start with " & strTrace & vbNewLine & strAddress
ActiveSheet.AutoFilterMode = False
End Sub
The expression
Rows("i:i").Select
throws an error -- Rows() won't recognize the text value "i:i" as an argument.
Rows(i).Select
will work. But it won't DO anything you can see, other than the last row should be selected when the code is finished running. You may want to do whatever needs to be done to the "T" rows at the next step in your code before your get to the Next i step.
EDIT:
OK, you want multiple rows selected when the code is finished. That can be done:
Dim RowsDescript As String
Dim trace As String
trace = "T"
Dim LR As Long, i As Long
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If Left(Range("M" & i).Value, 1) = trace Then RowsDescript = RowsDescript & i & ":" & i & ","
Next i
If Len(RowsDescript) > 0 Then
RowsDescript = Left(RowsDescript, Len(RowsDescript) - 1) ' removes the last comma
Range(RowsDescript).Select
End If
What you want to end up with is an expression that looks like this:
Range("9:9,12:12,16:16").Select
How you get there is, when a row is identified as having the "T" that you want in it, add the row number and a colon and the row number again and a comma to the string RowDescript. So at the end of the loop, you end up with the string having
9:9,12:12,16:16,
in it. But we need to strip off that last comma, so there's the check for a non-zero length string, remove the last character, and then select those rows.
1."T" is not equal to "t", to remove "case sensitivity" it is required to use LCase or UCase (low case or upper case)
2.Rows("i:i") replaced by Row(i)
Sub trace1()
Dim trace As String
trace = "T"
Dim LR As Long, i As Long
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If UCase(Left(Range("M" & i).Value, 1)) = UCase(trace) Then Rows(i).Select
Next i
End Sub
And also one comment, at the final will be selected only last row in range, for example row 1,5 and 10 will start from "T", so at the end will be selected only 10th row
updated against question in the comments
this will allow you to select rows which starting from "t" or "T", but this method allow to select not more than 45 rows.
Sub Macro1()
Dim trace$, LR&, i&, Rng$
trace = "T": Rng = ""
LR = Range("M" & Rows.Count).End(xlUp).Row
For i = 1 To LR
If UCase(Left(Range("M" & i).Value, 1)) = UCase(trace) Then Rng = Rng & i & ":" & i & ","
Next i
Rng = Left(Rng, Len(Rng) - 1)
Range(Rng).Select
End Sub
Use copy/paste one by one row if you need copy rows from one sheet to another, or sort range and select range from first row to the last row where cell value start from "T"

Add headers to column data using a macro

I'm in need of a simple macro that adds the column header values to the contents in the columns of a spreadsheet (preferably values that are specified).
So if possible, I'd like to specify the column names in VBA (Col1="Location") so that the macro is only applied to specific columns.
Example:
If I've specified, "Location" as a column header the macro should look for and A1 has "Location" as the header, then everything in A needs, "Location: " added to the front of it.
Basically, whatever the header is + ": ".
So this:
Location
A04B25
A05B89
B58C23
Would be this:
Location
Location: A04B25
Location: A05B89
Location: B58C23
This macro would need to cycle through each column and add that column header value to the values in the column IF it's on the list.
This is the code that I'm trying to use that isn't working:
Sub AppendHeader()
Dim i, LastCol
LastCol = Range("IV1").End(xlToLeft).Column
For i = 1 To LastCol
If UCase(Cells(1, i).Value) = "Local SKU" Then
Cells(1, i).EntireColumn.Append = UCase(Cells(1, i).Value) + ": "
End If
If UCase(Cells(1, i).Value) = "Supplier's SKU" Then
Cells(1, i).EntireColumn.Append = UCase(Cells(1, i).Value) + ": "
End If
Next
End Sub
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim preString As String
Dim lastRow As Long, LastCol As Long, i As Long, j As Long
Set ws = Sheets("Sheet1")
With ws
LastCol = .Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To LastCol
Select Case Trim(UCase(Cells(1, i).Value))
Case "LOCAL SKU", "SUPPLIER'S SKU"
lastRow = .Range(Split(Cells(, i).Address, "$")(1) & Rows.Count).End(xlUp).Row
preString = .Cells(1, i).Value & ": "
For j = 2 To lastRow
.Cells(j, i).Value = preString & .Cells(j, i).Value
Next j
End Select
Next i
End With
End Sub
There is a similar problem on SO, but I have come up with a different VBA solution. It will change the Number Format of the columns (except for the header row) based on that column's header.
To do this manually, you could select the "Custom" category for Format Cells and enter
"Location: "General;"Location: "#
This will make "Location: " show up in front of numbers, text, dates and such. Any formulas applied to these cells will take into account the prefix (Location:) but suppose you wanted to work with just the values. With this method, you can easily remove the formatting rather than creating a second subroutine to remove the prefix.
The code modifies Siddharth's -- Thank you, sir -- (I have not explicitly declared all the variables as he has, but that is best practice).
Sub Sample2()
Set ws = Sheets("Sheet1")
With ws
LastCol = .Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To LastCol
lastRow = .Range(Split(Cells(, i).Address, "$")(1) & Rows.Count).End(xlUp).Row
preString = .Cells(1, i).Value & ": "
Range(Cells(2, i), Cells(lastRow, i)).NumberFormat = _
Chr(34) & preString & Chr(34) & "General;" & _
Chr(34) & preString & Chr(34) & "#"
Next i
End With
End Sub