need to apply find method within the formula for all rows - vba

I am using vlookup in my formula in the macros . But it is taking too much time if it is applied for sheets containing more than 90k rows .
Instead i like to use find or Instr method to reduce the time . But i am getting error if i use "find: method . I dont how to use "Instr"
Sub testt()
Dim l As Long
l = Sheets(1).Range("A1:A" & Sheets(1).Cells(Sheets(1).Rows.Count, "A").End(xlUp).Row).Count
With Sheets("Sheet1")
.Range("d1").Formula = "=IF(iferror(vlookup(c1,$D:$D,1,false),"""")="""","""",1)"
.Range("d1").AutoFill Destination:=Range("d1:d" & l), Type:=xlFillDefault
End With
End Sub
here is my formula with vlookup . But i need to replace vlookup with find
l = Sheets(1).Range("A1:A" & Sheets(1).Cells(Sheets(1).Rows.Count, "A").End(xlUp).Row).Count
With Sheets("Sheet1")
.Range("d1").Formula = "=IF(iferror(range("A1:A" & l).find(c1),"""")="""","""",1)"
.Range("d1").AutoFill Destination:=Range("d1:d" & l), Type:=xlFillDefault
End With

There is no FIND function in Excel that I know of. However, you could write a VBA function to find each relevant cell in the lookup column. Here is the relevant bit of code:
Function IfFound(SearchText As String) As String
Dim FoundCell As Range
'look in particular column for a value
Set FoundCell = Columns("A:A").Find(What:=SearchText, lookat:=xlWhole)
'if found, return value in cell to its right
If FoundCell Is Nothing Then
IfFound = ""
Else
IfFound = FoundCell.Offset(0, 1).Value
End If
End Function
Here's how you could call this:
Sub TestFunction()
'make sure word TEST is in column A somewhere
MsgBox IfFound("test")
End Sub
Not sure if this is what you're looking for, but it would run much more quickly than VLOOKUP. The only problem is that you'd have to find a way to run it automatically whenever anything changed in the worksheet.

Related

How can I create a custom function using a variable-sized array input?

I am very new to advanced Excel use, so apologies in advance for anything I mis-identify.
In trying to automate some statistics gathering on a spreadsheet, I want to be able to define a custom function (either through a name definition or a VBA function) that can calculate a value with an array of indeterminate length.
Currently, the function I can copy-paste and change cell values is as follows:
=SUMPRODUCT((I46:I58="D")*(L46:L58<>"Anonymous"))+
SUMPRODUCT((I46:I58="Throwaway")*(L46:L58="Anonymous"))
Amending the array for every single use of the function is not optimal for the amount of data that I will be processing, and I'd like to find some way to have a name definition that automatically populates the arrays (perhaps by searching the I column for a specific value) or VBA function that populates the row numbers based on input cells.
I have tried a few different solutions based on searching for various functions, but I haven't been able to come up with a winning combination. Is there a better way to programatically populate the array values when the length is different for each function run?
Something like the following. You added additional information which is incomplete for range determination. You can use Range.Find method to get the cell where "Goal" is found and take the row number of that cell to determine the row end of your range. What is unknown is which column that is in and also how you are determining the "row of output" for your range start.
With that information you would use that range address in the function below.
Option Explicit
Public Sub test()
Debug.Print Foo(Range("L46:L58"))
Debug.Print Foo(Range("L46:L90"))
End Sub
Public Function Foo(ByVal rng As Range) As Double
rng.Parent.Evaluate ("SUMPRODUCT((" & rng.Address & "=""D"")*(" & rng.Address & "<>""Anonymous""))+SUMPRODUCT((" & rng.Address & "=""Throwaway"")*(" & rng.Address & "=""Anonymous""))")
End Function
Still not really sure what you're looking for, but this would be my best guess:
Function Counts()
Application.Volatile
Const FORMULA As String = _
"SUMPRODUCT(1*(I<r1>:I<r2>=""D""),1*(L<r1>:L<r2><>""Anonymous""))+" & _
"SUMPRODUCT(1*(I<r1>:I<r2>=""Throwaway""),1*(L<r1>:L<r2>=""Anonymous""))"
Dim clr As Range, f As Range, ws As Worksheet, rv, txt
Set clr = Application.ThisCell '<< this is the cell holding the formula
Set ws = clr.Parent '<< the worksheet
'find the first "Goal" in ColI
Set f = ws.Range(ws.Cells(clr.Row, "I"), _
ws.Cells(ws.Rows.Count, "I").End(xlUp)).Find(what:="Goal", lookat:=xlWhole)
If Not f Is Nothing Then
txt = Replace(FORMULA, "<r1>", clr.Row)
txt = Replace(txt, "<r2>", f.Row)
'Debug.Print txt
rv = ws.Evaluate(txt)
Else
rv = "No Goal!"
End If
Counts = rv
End Function
Looks like this:
Where the yellow-shaded cell has =COUNTS()

Excel VBA AutoFilter on user selection run-time error 1004

I have an Excel 2010 workbook containing 2 sheets ("Contents" and "Folders").
The purpose of this workbook is to track different pieces of work by supplier or reference number, with a front-end (the Contents page) that is simple to use, consisting only of buttons and a search box (Which isn't actually a separate box, but simply the contents of cell J8 of the Contents sheet (hereafter referred to as J8) as typed by the user.
The buttons will filter by supplier type (and work perfectly fine) but it's the user selection that I'm having trouble with.
My code for this macro is:
Sub Find_Click()
Dim userSelect As String
userSelect = "*" & Range("J8") & "*"
Sheets("Folders").Select
ActiveSheet.Range("$B$1:$B$5000").AutoFilter Field:=2, Criteria:=userSelect, Operator:=x1And
End Sub
When the 'Find' button is pressed, this should read J8, then select the Folders sheet and filter the result to show every entry in column B that contains the text in J8.
This was working fine. However, now when I try to use this macro I get a 1004 run-time error with the 'Application-defined or object-defined error' message.
Can anyone please help?
EDIT:
The Contains buttons that have macros assigned that follow this format:
Sub Button1_Click()
Sheets("Folders").Select
ActiveSheet.Range("$A$1:$A$5000").AutoFilter Field:=1, Criteria1:= _
"Criteria"
Set r = Range(Range("A3"), Range("A3").End(xlDown))
j = WorksheetFunction.CountA(r.Cells.SpecialCells(xlCellTypeVisible))
'MsgBox j
If j = 0 Then
MsgBox "There is currently no work relating to Criteria"
ActiveSheet.AutoFilterMode = False
ActiveSheet.Range("A3").Select
Sheets("Contents").Select
End If
End Sub
There is also a resest button that clears a filter and returns to the Contents sheet:
Sub Reset_Click()
ActiveSheet.ShowAllData
Sheets("Contents").Select
End Sub
Generally, you'll need to activate a cell inside the range in which you are going to use the AutoFilter.
Further more, when you are trying to use AutoFilter with wildcards (* or ?) or math test, you'll need to add an = at the start of your criteria string, so
userSelect = "=*" & Range("J8") & "*"
Then, it is not Criteria, but Criteria1 and Criteria2 if you use a second one! So you don't need an Operator in this case.
And finally with ActiveSheet.Range("$B$1:$B$5000").AutoFilter Field:=2, you are asking the code to filter on the second column of a range where there is only one column!
So if you want to filter on col B, just change Field:=2 to Field:=1
Here is the working code :
Sub Find_Click()
Dim userSelect As String
Dim wS as Worksheet
userSelect = "=*" & Range("J8") & "*"
Set wS = Sheets("Folders")
wS.Activate
wS.Range("B1").Activate
If Not wS.AutoFilterMode Then wS.AutoFilterMode = True
wS.Range("$B$1:$B$5000").AutoFilter Field:=1, Criteria1:=userSelect
End Sub
And you also had a typo in xlAnd, it was x1And ;)
For anyone who is interested, the problem ended up being in the line:
ActiveSheet.Range("$B$1:$B$5000").AutoFilter Field:=2, Criteria1:=userSelect
As the code was filtering only column B, the Field value needed to be set to '1' instead of my original '2'
Thanks to #R3uK for his invaluable help!

VBA search and copy

I'm automating an update I have to do and part of the macro I want to write needs specific text from what gets populated.
I have the following types of text in the same column for hundreds of rows:
ScreenRecording^naushi02^procr^10035
procr^10635^ScreenRecording^misby01
ScreenRecording^liw03^procr^10046
I've bold the text I need. I want to either replace the whole text with just what I need or place what I need in the next column, same row.
I had wrote something which worked for 60 or so lines before I realised that there are variations in the format. For the main, it's all the same which is why I didn't realise at first and I've spent a lot of wasted time writing something that is now useless... so I'm asking for expert help please.
Once I've got what I need from the first row, I need to move down until the last entry repeating.
I had some code which obviously didn't work fully.
I have thought about using the text 'ScreenRecording' in a search along with the special character which I can't find on my keyboard and then trying to copy all text from that point upto and including the 2nd numerical character. I don't know how to do this, if it would work or even if it's a good idea but because I've spent so much time trying to figure it out, I need some help please.
Thanks in advance
If you always want to return the value after the word 'ScreenRecording`, you can use the following function to do so.
Include it in a SubRoutine to replace in place if needed:
Function SplitScreenRecording(sInput As String) As String
Dim a As Variant
Const SDELIM As String = "^"
Const LOOKUP_VAL As String = "ScreenRecording"
a = Split(sInput, SDELIM)
If IsError(Application.Match(LOOKUP_VAL, a, 0)) Then
SplitScreenRecording = CVErr(2042)
Else
SplitScreenRecording = a(Application.Match(LOOKUP_VAL, a, 0))
End If
End Function
Sub ReplaceInPlace()
Dim rReplace As Range
Dim rng As Range
Set rReplace = Range("A1:A3")
For Each rng In rReplace
rng.Value = SplitScreenRecording(rng.Value)
Next rng
End Sub
if you want to replace:
Sub main2()
Dim key As String
Dim replacementStrng As String
key = "ScreenRecording"
replacementStrng = "AAA"
With Worksheets("mysheet01").columns("A") '<--| change "mysheet01" and "A" to your actual sheet name and column to filter
.Replace what:=key & "^*^", replacement:=key & "^" & replacementStrng & " ^ ", LookAt:=xlPart
.Replace what:="^" & key & "^*", replacement:="^" & key & "^" & replacementStrng, LookAt:=xlPart
End With
End Sub
while if you want to place what you need in the next column:
Sub main()
Dim myRng As Range
Set myRng = GetRange(Worksheets("mysheet01").columns("A"), "ScreenRecording^") '<--| change "mysheet01" and "A" to your actual sheet name and column to filter
myRng.Offset(, 1) = "value that I need to place in next row" '<--| change the right part of the assignment to what you need
End Sub
Function GetRange(rng As Range, key As String) As Range
With rng
.AutoFilter Field:=1, Criteria1:="*" & key & "*" '<--| apply current filtering
If Application.WorksheetFunction.Subtotal(103, .Cells) > 0 Then '<--| if there are visible cells other than the "header" one
With .SpecialCells(xlCellTypeConstants)
If InStr(.SpecialCells(xlCellTypeVisible).Cells(1, 1), key & "^") > 0 Then
Set GetRange = .SpecialCells(xlCellTypeVisible) '<--|select all visible cells
Else
Set GetRange = .Resize(.Parent.Cells(.Parent.Rows.Count, .Column).End(xlUp).row - 1).Offset(1).SpecialCells(xlCellTypeVisible) '<--|select visible rows other than the first ("headers") one
End If
End With
End If
.Parent.AutoFilterMode = False '<--| remove drop-down arrows
End With
End Function

For loop to change a specific cell in a formula

I have a formula that shows which rows in a specific column meet a set of criteria. When the formula is executed and applied to all rows, I run a loop to check which rows returned a value as a text, and then copy-pastes this cells to another worksheet:
Sub loop1()
Dim r As Range, c As Range
With Worksheets("Sheet1")
Set r = Range(.Range("AF2"), .Range("AF2").End(xlDown))
For Each c In r
If WorksheetFunction.IsText(c) Then
Range(.Cells(c.Row, "AF"), .Cells(c.Row, "AF")).Copy
Else
GoTo nextc
End If
With Worksheets("Sheet2")
.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End With
nextc:
Next c
End With
Application.CutCopyMode = False
End Sub
What I want to do now is to run the formula for 631 different names, copy-paste every name as a headline and then run loop1. I cant figure out though how to make the for loop work inside the formula.
Sub loop2()
Dim i As Integer
For i = 2 To 632
Sheets("Sheet1").Select
Range("AC2").Select
ActiveCell.FormulaR1C1 = _
"=IF(RC[-3]=""district1"",(IF(RC[2]=R2C33 ,(IF(RC[-18]>=1,0,(IF(RC[-16]>=1,0,IF(RC[-14]>=1,0,IF(RC[-12]>=1,0,IF(RC[-10]>=1,1,IF(RC[-8]>=1,1,IF(RC[-6]>=1,1,0))))))))),0)),0)"
Range("AC2").Select
Selection.AutoFill Destination:=Range("AC2:AC20753")
Range("AC2:AC20753").Select
Range("AG2").Select
Selection.Copy
Sheets("Sheet2").Select
ActiveSheet.Paste
Selection.Font.Bold = True
Sheets("Sheet1").Select
Application.Run "'Customers.xlsb'!loop1"
Next i
End Sub
The cells that need to be changed for every loop are, R2C33 to something like RiC33 (which doesn't work) and the "headline" Range("AG2").Select to something like Range("AGi").Select.
Anyone who could help?
The following code will do the trick:
Sub loop2()
Dim i As Integer
For i = 2 To 632
Sheets("Sheet1").Range("AC2:AC20753").FormulaR1C1 = _
"=IF(RC[-3]=""district1"",(IF(RC[2]=R" & i & "C33 ,(IF(RC[-18]>=1,0,(IF(RC[-16]>=1,0,IF(RC[-14]>=1,0,IF(RC[-12]>=1,0,IF(RC[-10]>=1,1,IF(RC[-8]>=1,1,IF(RC[-6]>=1,1,0))))))))),0)),0)"
Sheets("Sheet1").Range("AG" & i).Copy Destination:=Sheets("Sheet2").Range("A1")
Sheets("Sheet2").Range("A1").Font.Bold = True
Application.Run "'Customers.xlsb'!loop1"
Next i
End Sub
In order to let i be used within your String formula you have to stop the String " use & i & and continue the String ".
I have also changed your code to prevent the use of .Select, which is a no no in VBA.
This way it fills in your Formula copy's and changes the Font without selecting anything or changing sheets.
As Jeep noted you do however need to change Sheets(""Sheet2").Range("A1") as I don't know which cell you want to paste into.
Your first sub procedure might be better like this.
Sub loop1()
Dim r As Range, c As Range
With Worksheets("Sheet1")
Set r = Range(.Range("AF2"), .Range("AF2").End(xlDown))
For Each c In r
If WorksheetFunction.IsText(c) Then
Worksheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Offset(1, 0) = _
.Cells(c.Row, "AF").Value2
End If
Next c
End With
End Sub
Direct value transfer is preferred over a Copy, Paste Special, Values.
In the second sub procedure, you don't have to do anything but remove the 2 from R2C33; e.g. RC33. In xlR1C1 formula construction a lone R simply means the row that the formula is on and you are starting at row 2. You can also put all of the formulas in at once. Once they are in you can looop through the G2:G632 cells.
Sub loop2()
Dim i As Integer
With Sheets("Sheet1")
.Range("AC2:AC20753").FormulaR1C1 = _
"=IF(OR(AND(RC[-3]=""district1"", RC[2]=R2C33, RC[-18]>=1), SUM(RC[-16], RC[-14], RC[-12])>=1), 0, IF(SUM(RC[-10], RC[-8], RC[-6])>=1, 1, 0))"
For i = 2 To 632
.Range("AG" & i).Copy _
Destination:=Sheets("Sheet2").Somewhere
Sheets("Sheet2").Somewhere.Font.Bold = True
Application.Run "'Customers.xlsb'!loop1"
Next i
Next i
End Sub
I also tightened up your formula by grouping some of the conditions that would result in zero together with OR and AND functions.
The only thing remaining would be defining the Destination:=Sheets("Sheet2").Somewhere I left hanging.

macros vba need to apply formula to column till it has value in the last row

I need to apply "IF "formula in whole C column till has last value in the sheet using VBA .
but i am getting error 438 if i use the following code . Plz help me
Sub test11()
With Sheets("Sheet")
.Range("c1:c" & .Cells(.Rows.Count, "A").End(xlUp).Row).Formula = "=IF(B1="",TRIM(A1),TRIM(B1))"
End With
End Sub
So your sheet name is Sheet or Sheet1? And OP mentioned Sheet name is Sheet2. That removes one error. Secondly, you need to set D column as .Cells(.Rows.Count,"D").End(xlUp).Row) instead of A column.
Here is a very ugly code to try out: It takes last used row in count into the Long variable. Then set the range accordingly for setting up the formula using AutoFill.
Sub test11()
Dim l As Long
l = Sheets(1).Range("d1:d" & Sheets(1).Cells(Sheets(1).Rows.Count, "D").End(xlUp).Row).Count
With Sheets("Sheet1")
.Range("d1").Formula = "=IF(IsNull(B1),TRIM(A1),TRIM(B1))"
.Range("d1").AutoFill Destination:=Range("d1:d" & l), Type:=xlFillDefault
End With
End Sub
Your logic seems a bit strange, but this works:
Sub test11()
With Sheets("Sheet1")
.Range(.Range("c1"), .Range("C" & .Rows.Count).End(xlUp)) = "=IF(B1="""",TRIM(A1),TRIM(B1))"
End With
End Sub
You need to double the quotes within quotes in VBA.
Another variant:
Sub test12()
With Sheets("Sheet1")
Intersect(.Range("c1").CurrentRegion, .Range("C:C")).Formula = "=IF(B1="""",TRIM(A1),TRIM(B1))"
End With
End Sub