I am trying to create a VBA macro to automatically label each record in Sheet1 with the correct category based on Sheet2. However, I am getting this error:
Run-time Error '1004': Invalid number of arguments.
When I debug, it seems to point to the line of code noted below.
The codes are as follow in a module tab:
Sub X()
Dim i As Long
Dim r As Long
Dim s As Variant
For i = 1 To Worksheets("Sheet1").Rows.Count
Dim arr() As String
For r = 1 To Worksheets("Sheet2").Rows.Count
' the next line is where i get the error
If Application.IsNumber(Application.Search("Sheet2!A$" & i & ",Sheet1!A" & r)) Then
ReDim Preserve arr(i)
arr(i) = Worksheets("Sheet2").Cells(r, 2)
End If
Next r
For Each s In arr()
Dim b As Long
b = Application.Match(s & ",A1:M1")
Worksheets("Sheet1").Cells(r - 1, b).Value = "Y"
Next s
Erase arr()
Next i
End Sub
Sheet1 shows the special requests to be tagged/labelled with Y in the preceding columns
Sheet2 containing the primary keywords to search for and label the text in Sheet1 with Y if it is found
If you use Application.SomeFunction you should use the proper qualifier WorksheetFunction so you get the intellisense for the required parameters / arguments.
E.g typing Application.WorksheetFunction.Search( instead of Application.Search( will show that you need two string arguments in your Search() function, and you only provide one - but the one you provide does have a comma in it so I suspect you're joining the two arguments together in to one string by mistake.
You could try
("Sheet2!A$" & i, "Sheet1!A" & r)
In VBA this will become (Sheet2.Range("A1"), Sheet1.Range("A9"))
Instead of
("Sheet2!A$" & i & ",Sheet1!A" & r)
Which would resolve to ("Sheet2!A1,Sheet1!A9")
Note the difference - the latter is a single string which is trying to pass to Search() and the first is two separate Range arguments separated by a comma.
Just looking at the line you hightlighted.
If Application.IsNumber(Application.Search("Sheet2!A$" & i & ",Sheet1!A" & r)) Then
It appears:
Application.Search("Sheet2!A$" & i & ",Sheet1!A" & r)
requires two inputs. See link below
expression.Search (Arg1, Arg2, Arg3)
But, I could be wrong.
So the easiest way to debug would be to split up that line and see what is causing the error.
(1) Pull out this part of that line:
Application.Search("Sheet2!A$" & i & ",Sheet1!A" & r)
(2) Does that work? Does it return an argument that makes sense?
If not, fix that line. If so, move on to just the part below with a hardcoded input in the format from part 1 above. From there you can isolate which part of the line is giving you issues and verify each function is getting the right format input.
Application.IsNumber()
Related
I have an excel file with four columns: name, surname, address, area.
There are a lot of rows.
Is there a way to concatenate all the values of every single row in a variable, using vba?
I need a variable that should contain something like this:
(name1, surname1, address1, area1); (name2, surname2, address2, area2); (name3, surname3, address3, area3)...
If you have the following data in your worksheet
Then the following code will read the data into an array …
Option Explicit
Public Sub Example()
Dim RangeData() As Variant ' declare an array
RangeData = Range("A1:D5").Value2 ' read data into array
End Sub
… with the following structure:
Alternatively you can do something like
Public Sub Example()
Dim DataRange As Range
Set DataRange = Range("A2:D5")
Dim RetVal As String
Dim Row As Range
For Each Row In DataRange.Rows
RetVal = RetVal & "(" & Join(Application.Transpose(Application.Transpose(Row.Value2)), ",") & "); "
Next Row
Debug.Print RetVal
End Sub
To get this output:
(name1, surname1, address1, area1); (name2, surname2, address2, area2); (name3, surname3, address3, area3); (name4, surname4, address4, area4);
.. is there a way to write the result like a sort of list that shows all the values of the cells of the range?
Yes, there is. In addition to PEH's valid answers and disposing of Excel version MS365 you might also use
Dim s as String
s = Evaluate("ArrayToText(A2:D5, 1)") ' arg. value 1 representing strict format
resulting in the following output string:
{"name1","surname1","address1","area1";"name2","surname2","address2","area2";"name3","surname3","address3","area3";"name4","surname4","address4","area4"}
Syntax
ARRAYTOTEXT(array, [format])
The ARRAYTOTEXT function returns an array of text values from any specified range. It passes text values unchanged, and converts non-text values to text.
The format argument has two values, 0 (concise default format) and 1 (strict format to be used here to distinguish different rows, too):
Strict format, i.e. value 1 includes escape characters and row delimiters. Generates a string that can be parsed when entered into the formula bar. Encapsulates returned strings in quotes except for Booleans, Numbers and Errors.
Thank you for your answers, suggestions, ideas and hints. I am sorry if my question was not so clear, all the solutions you added were perfect and extremely elegant.
In the end I found a way - a dumber way in comparison to all the things you wrote - and I solved with a for statement.
I did like this:
totRow = ActiveSheet.UsedRange.Rows.Count
For i = 1 To totRow
name = Cells(i, 1)
surname = Cells(i, 2)
address = Cells(i, 3)
area = Cells(i, 4)
Example = Example & "(" & name & ", " & surname & ", " & address & ", " & area & "); "
Next i
Range("E1").Value = Example
It works (it does what I wanted to do), but I noticed a little limit: if the rows are a lot I can't keep the whole text in the variable.
I have a worksheet containing a column for the velocity of a vehicle, and I want to take specific ranges from that column (ranges where a turn occurs) and calculate the average speed of all turns combined.
I already have the turn ranges in two arrays, one for the turn starts and one for the turn ends. The code I wrote for this purpose looks like this:
Dim strTurnAvg As String
Dim k As Long
strTurnAvg = ""
For k = LBound(TurnStarts) To UBound(TurnStarts)
If TurnStarts(k) <> TurnEnds(k) Then 'the code for detecting turns rarely messes up and takes the same cell as the start and end of a turn. This is used to ignore such instances
If strTurnAvg = "" Then strTurnAvg = "Range(" & TurnStarts(k) & ":" & TurnEnds(k) & ").Value" Else strTurnAvg = strTurnAvg & ", Range(" & TurnStarts(k) & ":" & TurnEnds(k) & ").Value"
End If
Next
Dim result As Double
result = Application.WorksheetFunction.Average(strTurnAvg) '<- This results in a Run-time error '1004', Unable to get the Average property of the WorksheetFunction class
I tried manually putting the strTurnAvg result into an Average command, and everything worked perfectly fine, but when I try to pass it using the variable, it gives me the runtime error.
Is there a way to achieve this process in another way, or am I doing something wrong?
Try the following:
Dim TurnAvg As Range
Dim k As Long
For k = LBound(TurnStarts) To UBound(TurnStarts)
If TurnStarts(k) <> TurnEnds(k) Then 'the code for detecting turns rarely messes up and takes the same cell as the start and end of a turn. This is used to ignore such instances
If TurnAvg Is Nothing Then
Set TurnAvg = Range(TurnStarts(k), TurnEnds(k))
Else
Set TurnAvg = Union(TurnAvg, Range(TurnStarts(k), TurnEnds(k)))
End If
End If
Next
Dim Result As Double
Result = Application.WorksheetFunction.Average(TurnAvg)
It collects all the ranges using Union into TurnAvg. So you work with the actual ranges and the data in them. You cannot work with strings because you can only calculate with numbers but not with text. The Average functions needs numbers to calculate.
You might want to specify in which workbook/worksheet your ranges are like
ThisWorkbook.Worksheets("Sheet1").Range(TurnStarts(k), TurnEnds(k))
Otherwise VBA might pick the wrong workbook or wrong worksheet.
I'm trying to create a VBA-function in Excel where the number of inputs are unknown.
The inputs are all going to be numbers.
I've found information about Paramarray, but don't know it thats a good choice, and I am not able to make it work.
You stated that
I am not able to make it work.
However, you:
Have not posted the problematic code
have not posted your inputs
have not posted your expected results
have not posted your actual results
Given this lack of information, it is difficult to pinpoint your specific problem(s). Here is an example of a working function with variable number of inputs. Its purpose is to display for debugging, the values of several variables:
Public Function Displayy(ParamArray s()) As String
Dim i As Long
Displayy = CStr(s(0))
If UBound(s) = 0 Then Exit Function
For i = 1 To UBound(s)
Displayy = Displayy & vbCrLf & CStr(s(i))
Next i
End Function
Since your are building function, I am assuming the numbers are placed in Excel range (Example: A1: A10)
So you can use range as argument
Function YourFunctionName(rng as Range) as integer/Float/....etc
I would use variant. It can be range or array of numbers.
Public Function DisplayZ(Inputs As Variant) As String
Dim i As Long
Dim v As Variant
For Each v In Inputs
DisplayZ = DisplayZ & vbCrLf & v
Next v
End Function
I have two columns with data. The first one has some terms and the other one contains single words.
what I have
I'm looking for a way to identify which words from each cell from the first column appear in the second, so the result should look something like this (I don't need the commas):
what I need
My question is somehow similar to Excel find cells from range where search value is within the cell but not exactly, because I need to identify which words are appearing in the second column and there can be more than one word.
I also tried =INDEX($D$2:$D$7;MATCH(1=1;INDEX(ISNUMBER(SEARCH($D$2:$D$7;A2));0);))
but it also returns only one word.
If you are willing to use VBA, then you can define a user defined function:
Public Function SearchForWords(strTerm As String, rngWords As Range) As String
Dim cstrDelimiter As String: cstrDelimiter = Chr(1) ' A rarely used character
strTerm = cstrDelimiter & Replace(strTerm, " ", cstrDelimiter) & cstrDelimiter ' replace any other possible delimiter here
SearchForWords = vbNullString
Dim varWords As Variant: varWords = rngWords.Value
Dim i As Long: For i = LBound(varWords, 1) To UBound(varWords, 1)
Dim j As Long: For j = LBound(varWords, 2) To UBound(varWords, 2)
If InStr(1, strTerm, cstrDelimiter & varWords(i, j) & cstrDelimiter) <> 0 Then
SearchForWords = SearchForWords & varWords(i, j) & ", "
End If
Next j
Next i
Dim iLeft As Long: iLeft = Len(SearchForWords) - 2
If 0 < iLeft Then
SearchForWords = Left(SearchForWords, Len(SearchForWords) - 2)
End If
End Function
And you can use it from the Excel table like this:
=SearchForWords(A2;$D$2:$D$7)
I have a partial solution:
=IF(1-ISERROR(SEARCH(" "&D2:D7&" "," "&A2&" ")),D2:D7&", ","")
This formula returns an array of the words contained in the cell (ranges are according to your picture). This array is sparse: it contains empty strings for each missing word. And it assumes that words are always separated by one space (this may be improved if necessary).
However, native Excel functions are not capable of concatenating an array, so I think the rest is not possible with native formulas only.
You would need VBA but if you use VBA you should not bother with the first part at all, since you can do anything.
You can create a table with the words you want to find across the top and use a formula populate the cells below each word if it's found. See screenshot.
[edit] I've noticed that it's incorrectly picking up "board" in "blackboard" but that should be easily fixed.
=IFERROR(IF(FIND(C$1,$A2,1)>0,C$1 & ", "),"")
Simply concatinate the results
=CONCATENATE(C2,D2,E2,F2,G2,H2)
or
=LEFT(CONCATENATE(C2,D2,E2,F2,G2,H2),LEN(CONCATENATE(C2,D2,E2,F2,G2,H2))-2)
to take off the last comma and space
I've edited this to fix the problem with "blackboard"
new formula for C2
=IF(OR(C$1=$A2,ISNUMBER(SEARCH(" "&C$1&" ",$A2,1)),C$1 & " "=LEFT($A2,LEN(C$1)+1)," " & C$1=RIGHT($A2,LEN(C$1)+1)),C$1 & ", ","")
New formula for B2 to catch the error if there are no words
=IFERROR(LEFT(CONCATENATE(C2,D2,E2,F2,G2,H2,I2),LEN(CONCATENATE(C2,D2,E2,F2,G2,H2,I2))-2),"")
I am trying to write a VB script that concatenates data together into a url string and then I want it to copy down into all the rows for that column. I've got the fill down code working okay but when I try to add the concatenate I keep getting a syntax error so I'm not sure what I'm doing wrong. I have tried two versions and they both give me syntax errors on the final line of script (right side):
Script Version 1:
Sub SetSurveyLink()
' SetSurveyLink Macro
Dim lngLastRow As Long
lngLastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("C3:C" & lngLastRow).Value = EVALUATE("https://domainname.com/survey/?PartName=" & 'Client List'!B1 & "&ClientID="&B2)
End Sub
Script Version 2:
Sub SetSurveyLink()
' SetSurveyLink Macro
Dim lngLastRow As Long
lngLastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("C3:C" & lngLastRow).Value = CONCATENATE("https://domainname.com/survey/?PartName=",'Client List'!B1,"&ClientID=",B2)
End Sub
The 'concatenate' string gives me the correct value when used in a cell (i.e. not as part of a script) but I just can't get it to work in the script. See anything that I'm missing in my syntax?
THANK YOU!
Any double-quote marks within a string need to be escaped to be double double-quote marks so, if
"https://domainname.com/survey/?PartName=" & 'Client List'!B1 & "&ClientID="&B2
worked within Excel, it becomes
""https://domainname.com/survey/?PartName="" & 'Client List'!B1 & ""&ClientID=""&B2
and then you need to enclose that within double-quote marks to make it a string literal within VBA, i.e.
"""https://domainname.com/survey/?PartName="" & 'Client List'!B1 & ""&ClientID=""&B2"